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"
19#include "AchievementMgr.h"
20#include "ArenaTeamMgr.h"
21#include "Bag.h"
22#include "Chat.h"
23#include "Containers.h"
24#include "CreatureAIFactory.h"
25#include "DatabaseEnv.h"
26#include "DisableMgr.h"
27#include "GameObject.h"
28#include "GameObjectAIFactory.h"
29#include "GameTime.h"
30#include "GossipDef.h"
31#include "GroupMgr.h"
32#include "GuildMgr.h"
33#include "InstanceSaveMgr.h"
34#include "InstanceScript.h"
35#include "Language.h"
36#include "LFGMgr.h"
37#include "Log.h"
38#include "LootMgr.h"
39#include "Mail.h"
40#include "MapManager.h"
41#include "MotionMaster.h"
42#include "ObjectAccessor.h"
43#include "Player.h"
44#include "PoolMgr.h"
45#include "QueryPackets.h"
46#include "Random.h"
47#include "ReputationMgr.h"
48#include "ScriptMgr.h"
49#include "SpellAuras.h"
50#include "SpellMgr.h"
51#include "SpellScript.h"
52#include "StringConvert.h"
53#include "TemporarySummon.h"
54#include "ThreadPool.h"
55#include "UpdateMask.h"
56#include "Util.h"
57#include "Vehicle.h"
58#include "World.h"
59
62
64{
65 std::string res = "";
66 switch (type)
67 {
68 case SCRIPTS_EVENT: res = "event_scripts"; break;
69 case SCRIPTS_WAYPOINT: res = "waypoint_scripts"; break;
70 default: break;
71 }
72 return res;
73}
74
76{
77 ScriptMapMap* res = nullptr;
78 switch (type)
79 {
80 case SCRIPTS_EVENT: res = &sEventScripts; break;
81 case SCRIPTS_WAYPOINT: res = &sWaypointScripts; break;
82 default: break;
83 }
84 return res;
85}
86
88{
89 std::string res = "";
90 switch (command)
91 {
92 case SCRIPT_COMMAND_TALK: res = "SCRIPT_COMMAND_TALK"; break;
93 case SCRIPT_COMMAND_EMOTE: res = "SCRIPT_COMMAND_EMOTE"; break;
94 case SCRIPT_COMMAND_FIELD_SET: res = "SCRIPT_COMMAND_FIELD_SET"; break;
95 case SCRIPT_COMMAND_MOVE_TO: res = "SCRIPT_COMMAND_MOVE_TO"; break;
96 case SCRIPT_COMMAND_FLAG_SET: res = "SCRIPT_COMMAND_FLAG_SET"; break;
97 case SCRIPT_COMMAND_FLAG_REMOVE: res = "SCRIPT_COMMAND_FLAG_REMOVE"; break;
98 case SCRIPT_COMMAND_TELEPORT_TO: res = "SCRIPT_COMMAND_TELEPORT_TO"; break;
99 case SCRIPT_COMMAND_QUEST_EXPLORED: res = "SCRIPT_COMMAND_QUEST_EXPLORED"; break;
100 case SCRIPT_COMMAND_KILL_CREDIT: res = "SCRIPT_COMMAND_KILL_CREDIT"; break;
101 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT: res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT"; break;
102 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE: res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE"; break;
103 case SCRIPT_COMMAND_OPEN_DOOR: res = "SCRIPT_COMMAND_OPEN_DOOR"; break;
104 case SCRIPT_COMMAND_CLOSE_DOOR: res = "SCRIPT_COMMAND_CLOSE_DOOR"; break;
105 case SCRIPT_COMMAND_ACTIVATE_OBJECT: res = "SCRIPT_COMMAND_ACTIVATE_OBJECT"; break;
106 case SCRIPT_COMMAND_REMOVE_AURA: res = "SCRIPT_COMMAND_REMOVE_AURA"; break;
107 case SCRIPT_COMMAND_CAST_SPELL: res = "SCRIPT_COMMAND_CAST_SPELL"; break;
108 case SCRIPT_COMMAND_PLAY_SOUND: res = "SCRIPT_COMMAND_PLAY_SOUND"; break;
109 case SCRIPT_COMMAND_CREATE_ITEM: res = "SCRIPT_COMMAND_CREATE_ITEM"; break;
110 case SCRIPT_COMMAND_DESPAWN_SELF: res = "SCRIPT_COMMAND_DESPAWN_SELF"; break;
111 case SCRIPT_COMMAND_LOAD_PATH: res = "SCRIPT_COMMAND_LOAD_PATH"; break;
112 case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT: res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT"; break;
113 case SCRIPT_COMMAND_KILL: res = "SCRIPT_COMMAND_KILL"; break;
114 // TrinityCore only
115 case SCRIPT_COMMAND_ORIENTATION: res = "SCRIPT_COMMAND_ORIENTATION"; break;
116 case SCRIPT_COMMAND_EQUIP: res = "SCRIPT_COMMAND_EQUIP"; break;
117 case SCRIPT_COMMAND_MODEL: res = "SCRIPT_COMMAND_MODEL"; break;
118 case SCRIPT_COMMAND_CLOSE_GOSSIP: res = "SCRIPT_COMMAND_CLOSE_GOSSIP"; break;
119 case SCRIPT_COMMAND_PLAYMOVIE: res = "SCRIPT_COMMAND_PLAYMOVIE"; break;
120 case SCRIPT_COMMAND_MOVEMENT: res = "SCRIPT_COMMAND_MOVEMENT"; break;
121 default:
122 {
123 res = Trinity::StringFormat("Unknown command: {}", command);
124 break;
125 }
126 }
127 return res;
128}
129
130std::string ScriptInfo::GetDebugInfo() const
131{
132 return Trinity::StringFormat("{} ('{}' script id: {})", GetScriptCommandName(command), GetScriptsTableNameByType(type), id);
133}
134
135bool normalizePlayerName(std::string& name)
136{
137 if (name.empty())
138 return false;
139
140 std::wstring tmp;
141 if (!Utf8toWStr(name, tmp))
142 return false;
143
144 wstrToLower(tmp);
145 if (!tmp.empty())
146 tmp[0] = wcharToUpper(tmp[0]);
147
148 if (!WStrToUtf8(tmp, name))
149 return false;
150
151 return true;
152}
153
176
178{
179 for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
180 {
181 if (uint32(lang_description[i].lang_id) == lang)
182 return &lang_description[i];
183 }
184
185 return nullptr;
186}
187
188bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clickee) const
189{
190 Player const* playerClicker = clicker->ToPlayer();
191 if (!playerClicker)
192 return true;
193
194 Unit const* summoner = nullptr;
195 // Check summoners for party
196 if (clickee->IsSummon())
197 summoner = clickee->ToTempSummon()->GetSummonerUnit();
198 if (!summoner)
199 summoner = clickee;
200
201 // This only applies to players
202 switch (userType)
203 {
205 if (!playerClicker->IsFriendlyTo(summoner))
206 return false;
207 break;
209 if (!playerClicker->IsInRaidWith(summoner))
210 return false;
211 break;
213 if (!playerClicker->IsInPartyWith(summoner))
214 return false;
215 break;
216 default:
217 break;
218 }
219
220 return true;
221}
222
224 _auctionId(1),
225 _equipmentSetGuid(1),
226 _mailId(1),
227 _hiPetNumber(1),
228 _creatureSpawnId(1),
229 _gameObjectSpawnId(1),
230 DBCLocaleIndex(LOCALE_enUS)
231{
232}
233
235{
236 static ObjectMgr instance;
237 return &instance;
238}
239
243
244void ObjectMgr::AddLocaleString(std::string&& value, LocaleConstant localeConstant, std::vector<std::string>& data)
245{
246 if (!value.empty())
247 {
248 if (data.size() <= size_t(localeConstant))
249 data.resize(localeConstant + 1);
250
251 data[localeConstant] = std::move(value);
252 }
253}
254
256{
257 uint32 oldMSTime = getMSTime();
258
259 _creatureLocaleStore.clear(); // need for reload case
260
261 // 0 1 2 3
262 QueryResult result = WorldDatabase.Query("SELECT entry, locale, Name, Title FROM creature_template_locale");
263 if (!result)
264 return;
265
266 do
267 {
268 Field* fields = result->Fetch();
269
270 uint32 id = fields[0].GetUInt32();
271 std::string localeName = fields[1].GetString();
272
273 LocaleConstant locale = GetLocaleByName(localeName);
274 if (locale == LOCALE_enUS)
275 continue;
276
278 AddLocaleString(fields[2].GetString(), locale, data.Name);
279 AddLocaleString(fields[3].GetString(), locale, data.Title);
280 } while (result->NextRow());
281
282 TC_LOG_INFO("server.loading", ">> Loaded {} creature locale strings in {} ms", uint32(_creatureLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
283}
284
286{
287 uint32 oldMSTime = getMSTime();
288
289 _gossipMenuItemsLocaleStore.clear(); // need for reload case
290
291 // 0 1 2 3 4
292 QueryResult result = WorldDatabase.Query("SELECT MenuID, OptionID, Locale, OptionText, BoxText FROM gossip_menu_option_locale");
293
294 if (!result)
295 return;
296
297 do
298 {
299 Field* fields = result->Fetch();
300
301 uint16 menuId = fields[0].GetUInt16();
302 uint16 optionId = fields[1].GetUInt16();
303 std::string localeName = fields[2].GetString();
304
305 LocaleConstant locale = GetLocaleByName(localeName);
306 if (locale == LOCALE_enUS)
307 continue;
308
309 GossipMenuItemsLocale& data = _gossipMenuItemsLocaleStore[std::make_pair(menuId, optionId)];
310 AddLocaleString(fields[3].GetString(), locale, data.OptionText);
311 AddLocaleString(fields[4].GetString(), locale, data.BoxText);
312 } while (result->NextRow());
313
314 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option locale strings in {} ms", _gossipMenuItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
315}
316
318{
319 uint32 oldMSTime = getMSTime();
320
321 _pointOfInterestLocaleStore.clear(); // need for reload case
322
323 // 0 1 2
324 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM points_of_interest_locale");
325
326 if (!result)
327 return;
328
329 do
330 {
331 Field* fields = result->Fetch();
332
333 uint32 id = fields[0].GetUInt32();
334 std::string localeName = fields[1].GetString();
335
336 LocaleConstant locale = GetLocaleByName(localeName);
337 if (locale == LOCALE_enUS)
338 continue;
339
341 AddLocaleString(fields[2].GetString(), locale, data.Name);
342 } while (result->NextRow());
343
344 TC_LOG_INFO("server.loading", ">> Loaded {} points_of_interest locale strings in {} ms", uint32(_pointOfInterestLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
345}
346
348{
349 uint32 oldMSTime = getMSTime();
350
351 // Steps to update the counter below without doing it 1 by 1 manually
352 // 1. Using Notepad++ copy the query from "SELECT" to last field
353 // 2. Run this regex
354 // a.find "\r\n[ ]+\/\/[ ]+[0-9]+
355 // b.replace "\/\/
356 // 3. Alt + Left Click and vertical select all columns enough on the right of the file to be after // in all lines
357 // 4. Select "Edit" in the menu and then "Column Editor.."
358 // 5. Select "Number to Insert", Initial number 1, Increase by 1
359 // 6. Run this regex
360 // a.find "\/\/[ ]+
361 // b.replace "\r\n\t\t\/\/ (not that there is a space at the end of the regex, it's needed)
362
363 QueryResult result = WorldDatabase.Query(
364 // 0
365 "SELECT entry,"
366 // 1
367 "difficulty_entry_1,"
368 // 2
369 "difficulty_entry_2,"
370 // 3
371 "difficulty_entry_3,"
372 // 4
373 "KillCredit1,"
374 // 5
375 "KillCredit2,"
376 // 6
377 "modelid1,"
378 // 7
379 "modelid2,"
380 // 8
381 "modelid3,"
382 // 9
383 "modelid4,"
384 // 10
385 "name,"
386 // 11
387 "subname,"
388 // 12
389 "IconName,"
390 // 13
391 "gossip_menu_id,"
392 // 14
393 "minlevel,"
394 // 15
395 "maxlevel,"
396 // 16
397 "exp,"
398 // 17
399 "faction,"
400 // 18
401 "npcflag,"
402 // 19
403 "speed_walk,"
404 // 20
405 "speed_run,"
406 // 21
407 "scale,"
408 // 22
409 "`rank`,"
410 // 23
411 "dmgschool,"
412 // 24
413 "BaseAttackTime,"
414 // 25
415 "RangeAttackTime,"
416 // 26
417 "BaseVariance,"
418 // 27
419 "RangeVariance,"
420 // 28
421 "unit_class,"
422 // 29
423 "unit_flags,"
424 // 30
425 "unit_flags2,"
426 // 31
427 "dynamicflags,"
428 // 32
429 "family,"
430 // 33
431 "type,"
432 // 34
433 "type_flags,"
434 // 35
435 "lootid,"
436 // 36
437 "pickpocketloot,"
438 // 37
439 "skinloot,"
440 // 38
441 "PetSpellDataId,"
442 // 39
443 "VehicleId,"
444 // 40
445 "mingold,"
446 // 41
447 "maxgold,"
448 // 42
449 "AIName,"
450 // 43
451 "MovementType,"
452 // 44
453 "ctm.Ground,"
454 // 45
455 "ctm.Swim,"
456 // 46
457 "ctm.Flight,"
458 // 47
459 "ctm.Rooted,"
460 // 48
461 "ctm.Chase,"
462 // 49
463 "ctm.Random,"
464 // 50
465 "ctm.InteractionPauseTimer,"
466 // 51
467 "HoverHeight,"
468 // 52
469 "HealthModifier,"
470 // 53
471 "ManaModifier,"
472 // 54
473 "ArmorModifier,"
474 // 55
475 "DamageModifier,"
476 // 56
477 "ExperienceModifier,"
478 // 57
479 "RacialLeader,"
480 // 58
481 "movementId,"
482 // 59
483 "RegenHealth,"
484 // 60
485 "mechanic_immune_mask,"
486 // 61
487 "spell_school_immune_mask,"
488 // 62
489 "flags_extra,"
490 // 63
491 "ScriptName,"
492 // 64
493 "StringId"
494 " FROM creature_template ct"
495 " LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId");
496
497 if (!result)
498 {
499 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
500 return;
501 }
502
503 _creatureTemplateStore.reserve(result->GetRowCount());
504 do
505 {
506 Field* fields = result->Fetch();
507 LoadCreatureTemplate(fields);
508 } while (result->NextRow());
509
512
513 // Checking needs to be done after loading because of the difficulty self referencing
514 for (auto const& ctPair : _creatureTemplateStore)
515 CheckCreatureTemplate(&ctPair.second);
516
517 TC_LOG_INFO("server.loading", ">> Loaded {} creature definitions in {} ms", _creatureTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
518}
519
521{
522 uint32 entry = fields[0].GetUInt32();
523 CreatureTemplate& creatureTemplate = _creatureTemplateStore[entry];
524
525 creatureTemplate.Entry = entry;
526
527 for (uint8 i = 0; i < MAX_DIFFICULTY - 1; ++i)
528 creatureTemplate.DifficultyEntry[i] = fields[1 + i].GetUInt32();
529
530 for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
531 creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32();
532
533 creatureTemplate.Modelid1 = fields[6].GetUInt32();
534 creatureTemplate.Modelid2 = fields[7].GetUInt32();
535 creatureTemplate.Modelid3 = fields[8].GetUInt32();
536 creatureTemplate.Modelid4 = fields[9].GetUInt32();
537 creatureTemplate.Name = fields[10].GetString();
538 creatureTemplate.Title = fields[11].GetString();
539 creatureTemplate.IconName = fields[12].GetString();
540 creatureTemplate.GossipMenuId = fields[13].GetUInt32();
541 creatureTemplate.minlevel = fields[14].GetUInt8();
542 creatureTemplate.maxlevel = fields[15].GetUInt8();
543 creatureTemplate.expansion = uint32(fields[16].GetInt16());
544 creatureTemplate.faction = fields[17].GetUInt16();
545 creatureTemplate.npcflag = fields[18].GetUInt32();
546 creatureTemplate.speed_walk = fields[19].GetFloat();
547 creatureTemplate.speed_run = fields[20].GetFloat();
548 creatureTemplate.scale = fields[21].GetFloat();
549 creatureTemplate.rank = fields[22].GetUInt8();
550 creatureTemplate.dmgschool = uint32(fields[23].GetInt8());
551 creatureTemplate.BaseAttackTime = fields[24].GetUInt32();
552 creatureTemplate.RangeAttackTime = fields[25].GetUInt32();
553 creatureTemplate.BaseVariance = fields[26].GetFloat();
554 creatureTemplate.RangeVariance = fields[27].GetFloat();
555 creatureTemplate.unit_class = fields[28].GetUInt8();
556 creatureTemplate.unit_flags = fields[29].GetUInt32();
557 creatureTemplate.unit_flags2 = fields[30].GetUInt32();
558 creatureTemplate.dynamicflags = fields[31].GetUInt32();
559 creatureTemplate.family = CreatureFamily(fields[32].GetUInt8());
560 creatureTemplate.type = fields[33].GetUInt8();
561 creatureTemplate.type_flags = fields[34].GetUInt32();
562 creatureTemplate.lootid = fields[35].GetUInt32();
563 creatureTemplate.pickpocketLootId = fields[36].GetUInt32();
564 creatureTemplate.SkinLootId = fields[37].GetUInt32();
565
566 for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
567 creatureTemplate.resistance[i] = 0;
568
569 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
570 creatureTemplate.spells[i] = 0;
571
572 creatureTemplate.PetSpellDataId = fields[38].GetUInt32();
573 creatureTemplate.VehicleId = fields[39].GetUInt32();
574 creatureTemplate.mingold = fields[40].GetUInt32();
575 creatureTemplate.maxgold = fields[41].GetUInt32();
576 creatureTemplate.AIName = fields[42].GetString();
577 creatureTemplate.MovementType = fields[43].GetUInt8();
578 if (!fields[44].IsNull())
579 creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[44].GetUInt8());
580
581 if (!fields[45].IsNull())
582 creatureTemplate.Movement.Swim = fields[45].GetBool();
583
584 if (!fields[46].IsNull())
585 creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[46].GetUInt8());
586
587 if (!fields[47].IsNull())
588 creatureTemplate.Movement.Rooted = fields[47].GetBool();
589
590 if (!fields[48].IsNull())
591 creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[48].GetUInt8());
592
593 if (!fields[49].IsNull())
594 creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[49].GetUInt8());
595
596 if (!fields[50].IsNull())
597 creatureTemplate.Movement.InteractionPauseTimer = fields[50].GetUInt32();
598
599 creatureTemplate.HoverHeight = fields[51].GetFloat();
600 creatureTemplate.ModHealth = fields[52].GetFloat();
601 creatureTemplate.ModMana = fields[53].GetFloat();
602 creatureTemplate.ModArmor = fields[54].GetFloat();
603 creatureTemplate.ModDamage = fields[55].GetFloat();
604 creatureTemplate.ModExperience = fields[56].GetFloat();
605 creatureTemplate.RacialLeader = fields[57].GetBool();
606
607 creatureTemplate.movementId = fields[58].GetUInt32();
608 creatureTemplate.RegenHealth = fields[59].GetBool();
609 creatureTemplate.MechanicImmuneMask = fields[60].GetUInt32();
610 creatureTemplate.SpellSchoolImmuneMask = fields[61].GetUInt32();
611 creatureTemplate.flags_extra = fields[62].GetUInt32();
612 creatureTemplate.ScriptID = GetScriptId(fields[63].GetString());
613 creatureTemplate.StringId = fields[64].GetString();
614}
615
617{
618 uint32 oldMSTime = getMSTime();
619
620 // 0 1 2
621 QueryResult result = WorldDatabase.Query("SELECT CreatureID, School, Resistance FROM creature_template_resistance");
622
623 if (!result)
624 {
625 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template resistance definitions. DB table `creature_template_resistance` is empty.");
626 return;
627 }
628
629 uint32 count = 0;
630
631 do
632 {
633 Field* fields = result->Fetch();
634
635 uint32 creatureID = fields[0].GetUInt32();
636 uint8 school = fields[1].GetUInt8();
637
638 if (school == SPELL_SCHOOL_NORMAL || school >= MAX_SPELL_SCHOOL)
639 {
640 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this school {} doesn't exist", creatureID, school);
641 continue;
642 }
643
644 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
645 if (itr == _creatureTemplateStore.end())
646 {
647 TC_LOG_ERROR("sql.sql", "creature_template_resistance has resistance definitions for creature {} but this creature doesn't exist", creatureID);
648 continue;
649 }
650
651 CreatureTemplate& creatureTemplate = itr->second;
652 creatureTemplate.resistance[school] = fields[2].GetInt16();
653
654 ++count;
655
656 } while (result->NextRow());
657
658 TC_LOG_INFO("server.loading", ">> Loaded {} creature template resistances in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
659}
660
662{
663 uint32 oldMSTime = getMSTime();
664
665 // 0 1 2
666 QueryResult result = WorldDatabase.Query("SELECT CreatureID, `Index`, Spell FROM creature_template_spell");
667
668 if (!result)
669 {
670 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template spell definitions. DB table `creature_template_spell` is empty.");
671 return;
672 }
673
674 uint32 count = 0;
675
676 do
677 {
678 Field* fields = result->Fetch();
679
680 uint32 creatureID = fields[0].GetUInt32();
681 uint8 index = fields[1].GetUInt8();
682
683 if (index >= MAX_CREATURE_SPELLS)
684 {
685 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} with a incorrect index {}", creatureID, index);
686 continue;
687 }
688
689 CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID);
690 if (itr == _creatureTemplateStore.end())
691 {
692 TC_LOG_ERROR("sql.sql", "creature_template_spell has spell definitions for creature {} but this creature doesn't exist", creatureID);
693 continue;
694 }
695
696 CreatureTemplate& creatureTemplate = itr->second;
697 creatureTemplate.spells[index] = fields[2].GetUInt32();;
698
699 ++count;
700
701 } while (result->NextRow());
702
703 TC_LOG_INFO("server.loading", ">> Loaded {} creature template spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
704}
705
707{
708 uint32 oldMSTime = getMSTime();
709
710 // 0 1 2 3 4 5 6 7 8 9 10
711 QueryResult result = WorldDatabase.Query("SELECT entry, path_id, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, visibilityDistanceType, auras FROM creature_template_addon");
712
713 if (!result)
714 {
715 TC_LOG_INFO("server.loading", ">> Loaded 0 creature template addon definitions. DB table `creature_template_addon` is empty.");
716 return;
717 }
718
719 uint32 count = 0;
720 do
721 {
722 Field* fields = result->Fetch();
723
724 uint32 entry = fields[0].GetUInt32();
725
726 if (!sObjectMgr->GetCreatureTemplate(entry))
727 {
728 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_template_addon`", entry);
729 continue;
730 }
731
732 CreatureAddon& creatureAddon = _creatureTemplateAddonStore[entry];
733
734 creatureAddon.path_id = fields[1].GetUInt32();
735 creatureAddon.mount = fields[2].GetUInt32();
736 creatureAddon.standState = fields[3].GetUInt8();
737 creatureAddon.animTier = fields[4].GetUInt8();
738 creatureAddon.visFlags = fields[5].GetUInt8();
739 creatureAddon.sheathState = fields[6].GetUInt8();
740 creatureAddon.pvpFlags = fields[7].GetUInt8();
741 creatureAddon.emote = fields[8].GetUInt32();
742 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[9].GetUInt8());
743
744 for (std::string_view aura : Trinity::Tokenize(fields[10].GetStringView(), ' ', false))
745 {
746
747 SpellInfo const* spellInfo = nullptr;
748 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
749 spellInfo = sSpellMgr->GetSpellInfo(*spellId);
750
751 if (!spellInfo)
752 {
753 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong spell '{}' defined in `auras` field in `creature_template_addon`.", entry, aura);
754 continue;
755 }
756
757 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
758 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
759
760 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
761 {
762 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has duplicate aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
763 continue;
764 }
765
766 if (spellInfo->GetDuration() > 0)
767 {
768 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has temporary aura (spell {}) in `auras` field in `creature_template_addon`.", entry, spellInfo->Id);
769 continue;
770 }
771
772 creatureAddon.auras.push_back(spellInfo->Id);
773 }
774
775 if (creatureAddon.mount)
776 {
777 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
778 {
779 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid displayInfoId ({}) for mount defined in `creature_template_addon`", entry, creatureAddon.mount);
780 creatureAddon.mount = 0;
781 }
782 }
783
784 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
785 {
786 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid unit stand state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.standState);
787 creatureAddon.standState = 0;
788 }
789
790 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
791 {
792 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid animation tier ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.animTier);
793 creatureAddon.animTier = 0;
794 }
795
796 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
797 {
798 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid sheath state ({}) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.sheathState);
799 creatureAddon.sheathState = 0;
800 }
801
802 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
803
804 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
805 {
806 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid emote ({}) defined in `creature_template_addon`.", entry, creatureAddon.emote);
807 creatureAddon.emote = 0;
808 }
809
811 {
812 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid visibilityDistanceType ({}) defined in `creature_template_addon`.",
813 entry, AsUnderlyingType(creatureAddon.visibilityDistanceType));
815 }
816
817 ++count;
818 }
819 while (result->NextRow());
820
821 TC_LOG_INFO("server.loading", ">> Loaded {} creature template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
822}
823
825{
826 if (!cInfo)
827 return;
828
829 bool ok = true; // bool to allow continue outside this loop
830 for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
831 {
832 if (!cInfo->DifficultyEntry[diff])
833 continue;
834 ok = false; // will be set to true at the end of this loop again
835
836 CreatureTemplate const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
837 if (!difficultyInfo)
838 {
839 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `difficulty_entry_{}`={} but creature entry {} does not exist.",
840 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
841 continue;
842 }
843
844 bool ok2 = true;
845 for (uint32 diff2 = 0; diff2 < MAX_DIFFICULTY - 1 && ok2; ++diff2)
846 {
847 ok2 = false;
848 if (_difficultyEntries[diff2].find(cInfo->Entry) != _difficultyEntries[diff2].end())
849 {
850 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) is listed as `difficulty_entry_{}` of another creature, but itself lists {} in `difficulty_entry_{}`.",
851 cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1);
852 continue;
853 }
854
855 if (_difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _difficultyEntries[diff2].end())
856 {
857 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) already listed as `difficulty_entry_{}` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
858 continue;
859 }
860
861 if (_hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != _hasDifficultyEntries[diff2].end())
862 {
863 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `difficulty_entry_{}`={} but creature entry {} has itself a value in `difficulty_entry_{}`.",
864 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
865 continue;
866 }
867 ok2 = true;
868 }
869
870 if (!ok2)
871 continue;
872
873 if (cInfo->expansion > difficultyInfo->expansion)
874 {
875 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, exp: {}) has different `exp` in difficulty {} mode (Entry: {}, exp: {}).",
876 cInfo->Entry, cInfo->expansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->expansion);
877 }
878
879 if (cInfo->minlevel > difficultyInfo->minlevel)
880 {
881 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, minlevel: {}) has lower `minlevel` in difficulty {} mode (Entry: {}, minlevel: {}).",
882 cInfo->Entry, cInfo->minlevel, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->minlevel);
883 }
884
885 if (cInfo->maxlevel > difficultyInfo->maxlevel)
886 {
887 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, maxlevel: {}) has lower `maxlevel` in difficulty {} mode (Entry: {}, maxlevel: {}).",
888 cInfo->Entry, cInfo->maxlevel, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->maxlevel);
889 }
890
891 if (cInfo->faction != difficultyInfo->faction)
892 {
893 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, faction: {}) has different `faction` in difficulty {} mode (Entry: {}, faction: {}).",
894 cInfo->Entry, cInfo->faction, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->faction);
895 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `faction`={} WHERE `entry`={};",
896 cInfo->faction, cInfo->DifficultyEntry[diff]);
897 }
898
899 if (cInfo->unit_class != difficultyInfo->unit_class)
900 {
901 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, class: {}) has different `unit_class` in difficulty {} mode (Entry: {}, class: {}).",
902 cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
903 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_class`={} WHERE `entry`={};",
904 cInfo->unit_class, cInfo->DifficultyEntry[diff]);
905 continue;
906 }
907
908 uint32 differenceMask = cInfo->npcflag ^ difficultyInfo->npcflag;
909 if (cInfo->npcflag != difficultyInfo->npcflag)
910 {
911 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `npcflag`: {}) has different `npcflag` in difficulty {} mode (Entry: {}, `npcflag`: {}).",
912 cInfo->Entry, cInfo->npcflag, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->npcflag);
913 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `npcflag`=`npcflag`^{} WHERE `entry`={};",
914 differenceMask, cInfo->DifficultyEntry[diff]);
915 }
916
917 if (cInfo->dmgschool != difficultyInfo->dmgschool)
918 {
919 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `dmgschool`: {}) has different `dmgschool` in difficulty {} mode (Entry: {}, `dmgschool`: {}).",
920 cInfo->Entry, cInfo->dmgschool, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->dmgschool);
921 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `dmgschool`={} WHERE `entry`={};",
922 cInfo->dmgschool, cInfo->DifficultyEntry[diff]);
923 }
924
925 differenceMask = cInfo->unit_flags2 ^ difficultyInfo->unit_flags2;
926 if (cInfo->unit_flags2 != difficultyInfo->unit_flags2)
927 {
928 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, `unit_flags2`: {}) has different `unit_flags2` in difficulty {} mode (Entry: {}, `unit_flags2`: {}).",
929 cInfo->Entry, cInfo->unit_flags2, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_flags2);
930 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `unit_flags2`=`unit_flags2`^{} WHERE `entry`={};",
931 differenceMask, cInfo->DifficultyEntry[diff]);
932 }
933
934 if (cInfo->family != difficultyInfo->family)
935 {
936 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, family: {}) has different `family` in difficulty {} mode (Entry: {}, family: {}).",
937 cInfo->Entry, cInfo->family, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->family);
938 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `family`={} WHERE `entry`={};",
939 cInfo->family, cInfo->DifficultyEntry[diff]);
940 }
941
942 if (cInfo->type != difficultyInfo->type)
943 {
944 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, type: {}) has different `type` in difficulty {} mode (Entry: {}, type: {}).",
945 cInfo->Entry, cInfo->type, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->type);
946 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `type`={} WHERE `entry`={};",
947 cInfo->type, cInfo->DifficultyEntry[diff]);
948 }
949
950 if (!cInfo->VehicleId && difficultyInfo->VehicleId)
951 {
952 TC_LOG_ERROR("sql.sql", "Non-vehicle Creature (Entry: {}, VehicleId: {}) has `VehicleId` set in difficulty {} mode (Entry: {}, VehicleId: {}).",
953 cInfo->Entry, cInfo->VehicleId, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->VehicleId);
954 }
955
956 if (cInfo->RegenHealth != difficultyInfo->RegenHealth)
957 {
958 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, RegenHealth: {}) has different `RegenHealth` in difficulty {} mode (Entry: {}, RegenHealth: {}).",
959 cInfo->Entry, cInfo->RegenHealth, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->RegenHealth);
960 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `RegenHealth`={} WHERE `entry`={};",
961 cInfo->RegenHealth, cInfo->DifficultyEntry[diff]);
962 }
963
964 differenceMask = cInfo->MechanicImmuneMask & (~difficultyInfo->MechanicImmuneMask);
965 if (differenceMask)
966 {
967 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, mechanic_immune_mask: {}) has weaker immunities in difficulty {} mode (Entry: {}, mechanic_immune_mask: {}).",
968 cInfo->Entry, cInfo->MechanicImmuneMask, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->MechanicImmuneMask);
969 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `mechanic_immune_mask`=`mechanic_immune_mask`|{} WHERE `entry`={};",
970 differenceMask, cInfo->DifficultyEntry[diff]);
971 }
972
973 differenceMask = (cInfo->flags_extra ^ difficultyInfo->flags_extra) & (~CREATURE_FLAG_EXTRA_INSTANCE_BIND);
974 if (differenceMask)
975 {
976 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}, flags_extra: {}) has different `flags_extra` in difficulty {} mode (Entry: {}, flags_extra: {}).",
977 cInfo->Entry, cInfo->flags_extra, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->flags_extra);
978 TC_LOG_ERROR("sql.sql", "Possible FIX: UPDATE `creature_template` SET `flags_extra`=`flags_extra`^{} WHERE `entry`={};",
979 differenceMask, cInfo->DifficultyEntry[diff]);
980 }
981
982 if (!difficultyInfo->AIName.empty())
983 {
984 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists difficulty {} mode entry {} with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.",
985 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
986 continue;
987 }
988
989 if (difficultyInfo->ScriptID)
990 {
991 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists difficulty {} mode entry {} with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.",
992 cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
993 continue;
994 }
995
996 _hasDifficultyEntries[diff].insert(cInfo->Entry);
997 _difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
998 ok = true;
999 }
1000
1001 if (cInfo->mingold > cInfo->maxgold)
1002 {
1003 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has `mingold` {} which is greater than `maxgold` {}, setting `maxgold` to {}.",
1004 cInfo->Entry, cInfo->mingold, cInfo->maxgold, cInfo->mingold);
1005 const_cast<CreatureTemplate*>(cInfo)->maxgold = cInfo->mingold;
1006 }
1007
1008 if (!cInfo->AIName.empty())
1009 {
1010 auto registryItem = sCreatureAIRegistry->GetRegistryItem(cInfo->AIName);
1011 if (!registryItem)
1012 {
1013 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-registered `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1014 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1015 }
1016 else
1017 {
1018 DBPermit const* permit = dynamic_cast<DBPermit const*>(registryItem);
1019 if (!ASSERT_NOTNULL(permit)->IsScriptNameAllowedInDB())
1020 {
1021 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has not-allowed `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
1022 const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
1023 }
1024 }
1025 }
1026
1027 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
1028 if (!factionTemplate)
1029 {
1030 TC_LOG_FATAL("sql.sql", "Creature (Entry: {}) has non-existing faction template ({}). This can lead to crashes, aborting.", cInfo->Entry, cInfo->faction);
1031 ABORT();
1032 }
1033
1034 // used later for scale
1035 CreatureDisplayInfoEntry const* displayScaleEntry = nullptr;
1036
1037 if (cInfo->Modelid1)
1038 {
1039 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
1040 if (!displayEntry)
1041 {
1042 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid1 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
1043 const_cast<CreatureTemplate*>(cInfo)->Modelid1 = 0;
1044 }
1045 else
1046 displayScaleEntry = displayEntry;
1047
1048 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
1049 if (!modelInfo)
1050 TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = {} listed by creature (Entry: {}).", cInfo->Modelid1, cInfo->Entry);
1051 }
1052
1053 if (cInfo->Modelid2)
1054 {
1055 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
1056 if (!displayEntry)
1057 {
1058 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid2 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid2);
1059 const_cast<CreatureTemplate*>(cInfo)->Modelid2 = 0;
1060 }
1061 else if (!displayScaleEntry)
1062 displayScaleEntry = displayEntry;
1063
1064 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2);
1065 if (!modelInfo)
1066 TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = {} listed by creature (Entry: {}).", cInfo->Modelid2, cInfo->Entry);
1067 }
1068
1069 if (cInfo->Modelid3)
1070 {
1071 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
1072 if (!displayEntry)
1073 {
1074 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid3 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid3);
1075 const_cast<CreatureTemplate*>(cInfo)->Modelid3 = 0;
1076 }
1077 else if (!displayScaleEntry)
1078 displayScaleEntry = displayEntry;
1079
1080 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3);
1081 if (!modelInfo)
1082 TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = {} listed by creature (Entry: {}).", cInfo->Modelid3, cInfo->Entry);
1083 }
1084
1085 if (cInfo->Modelid4)
1086 {
1087 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
1088 if (!displayEntry)
1089 {
1090 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing Modelid4 id ({}), this can crash the client.", cInfo->Entry, cInfo->Modelid4);
1091 const_cast<CreatureTemplate*>(cInfo)->Modelid4 = 0;
1092 }
1093 else if (!displayScaleEntry)
1094 displayScaleEntry = displayEntry;
1095
1096 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4);
1097 if (!modelInfo)
1098 TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = {} listed by creature (Entry: {}).", cInfo->Modelid4, cInfo->Entry);
1099 }
1100
1101 if (!displayScaleEntry)
1102 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);
1103
1104 for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k)
1105 {
1106 if (cInfo->KillCredit[k])
1107 {
1108 if (!GetCreatureTemplate(cInfo->KillCredit[k]))
1109 {
1110 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) lists non-existing creature entry {} in `KillCredit{}`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
1111 const_cast<CreatureTemplate*>(cInfo)->KillCredit[k] = 0;
1112 }
1113 }
1114 }
1115
1116 if (!cInfo->unit_class || ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
1117 {
1118 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);
1119 const_cast<CreatureTemplate*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
1120 }
1121
1122 if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
1123 {
1124 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid spell school value ({}) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
1125 const_cast<CreatureTemplate*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
1126 }
1127
1128 if (cInfo->BaseAttackTime == 0)
1129 const_cast<CreatureTemplate*>(cInfo)->BaseAttackTime = BASE_ATTACK_TIME;
1130
1131 if (cInfo->RangeAttackTime == 0)
1132 const_cast<CreatureTemplate*>(cInfo)->RangeAttackTime = BASE_ATTACK_TIME;
1133
1134 if (cInfo->speed_walk == 0.0f)
1135 {
1136 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_walk, set to 1.", cInfo->Entry, cInfo->speed_walk);
1137 const_cast<CreatureTemplate*>(cInfo)->speed_walk = 1.0f;
1138 }
1139
1140 if (cInfo->speed_run == 0.0f)
1141 {
1142 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in speed_run, set to 1.14286.", cInfo->Entry, cInfo->speed_run);
1143 const_cast<CreatureTemplate*>(cInfo)->speed_run = 1.14286f;
1144 }
1145
1146 if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
1147 {
1148 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature type ({}) in `type`.", cInfo->Entry, cInfo->type);
1149 const_cast<CreatureTemplate*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
1150 }
1151
1152 // must exist or used hidden but used in data horse case
1153 if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
1154 {
1155 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has invalid creature family ({}) in `family`.", cInfo->Entry, cInfo->family);
1156 const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE;
1157 }
1158
1159 CheckCreatureMovement("creature_template_movement", cInfo->Entry, const_cast<CreatureTemplate*>(cInfo)->Movement);
1160
1161 if (cInfo->HoverHeight < 0.0f)
1162 {
1163 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong value ({}) in `HoverHeight`", cInfo->Entry, cInfo->HoverHeight);
1164 const_cast<CreatureTemplate*>(cInfo)->HoverHeight = 1.0f;
1165 }
1166
1167 if (cInfo->VehicleId)
1168 {
1169 VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
1170 if (!vehId)
1171 {
1172 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has a non-existing VehicleId ({}). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
1173 const_cast<CreatureTemplate*>(cInfo)->VehicleId = 0;
1174 }
1175 }
1176
1177 if (cInfo->PetSpellDataId)
1178 {
1179 CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
1180 if (!spellDataId)
1181 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing PetSpellDataId ({}).", cInfo->Entry, cInfo->PetSpellDataId);
1182 }
1183
1184 for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j)
1185 {
1186 if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j]))
1187 {
1188 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing Spell{} ({}), set to 0.", cInfo->Entry, j+1, cInfo->spells[j]);
1189 const_cast<CreatureTemplate*>(cInfo)->spells[j] = 0;
1190 }
1191 }
1192
1193 if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
1194 {
1195 TC_LOG_ERROR("sql.sql", "Creature (Entry: {}) has wrong movement generator type ({}), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
1196 const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
1197 }
1198
1200 if (cInfo->scale <= 0.0f)
1201 {
1202 if (displayScaleEntry)
1203 const_cast<CreatureTemplate*>(cInfo)->scale = displayScaleEntry->CreatureModelScale;
1204 else
1205 const_cast<CreatureTemplate*>(cInfo)->scale = 1.0f;
1206 }
1207
1208 if (cInfo->expansion > (MAX_EXPANSIONS - 1))
1209 {
1210 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with expansion {}. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
1211 const_cast<CreatureTemplate*>(cInfo)->expansion = 0;
1212 }
1213
1214 if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
1215 {
1216 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `flags_extra` {}, removing incorrect flag.", cInfo->Entry, badFlags);
1217 const_cast<CreatureTemplate*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
1218 }
1219
1220 if (uint32 disallowedUnitFlags = (cInfo->unit_flags & ~UNIT_FLAG_ALLOWED))
1221 {
1222 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags);
1223 const_cast<CreatureTemplate*>(cInfo)->unit_flags &= UNIT_FLAG_ALLOWED;
1224 }
1225
1226 if (uint32 disallowedUnitFlags2 = (cInfo->unit_flags2 & ~UNIT_FLAG2_ALLOWED))
1227 {
1228 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with disallowed `unit_flags2` {}, removing incorrect flag.", cInfo->Entry, disallowedUnitFlags2);
1229 const_cast<CreatureTemplate*>(cInfo)->unit_flags2 &= UNIT_FLAG2_ALLOWED;
1230 }
1231
1232 if (cInfo->dynamicflags)
1233 {
1234 TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: {}) with `dynamicflags` > 0. Ignored and set to 0.", cInfo->Entry);
1235 const_cast<CreatureTemplate*>(cInfo)->dynamicflags = 0;
1236 }
1237
1238 const_cast<CreatureTemplate*>(cInfo)->ModDamage *= Creature::_GetDamageMod(cInfo->rank);
1239
1240 if (cInfo->GossipMenuId && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP))
1241 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu {}, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry, cInfo->GossipMenuId);
1242 else if (!cInfo->GossipMenuId && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)
1243 TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has npcflag UNIT_NPC_FLAG_GOSSIP, but gossip menu is unassigned.", cInfo->Entry);
1244}
1245
1246void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement)
1247{
1248 if (creatureMovement.Ground >= CreatureGroundMovementType::Max)
1249 {
1250 TC_LOG_ERROR("sql.sql", "`{}`.`Ground` wrong value ({}) for Id {}, setting to Run.",
1251 table, uint32(creatureMovement.Ground), id);
1252 creatureMovement.Ground = CreatureGroundMovementType::Run;
1253 }
1254
1255 if (creatureMovement.Flight >= CreatureFlightMovementType::Max)
1256 {
1257 TC_LOG_ERROR("sql.sql", "`{}`.`Flight` wrong value ({}) for Id {}, setting to None.",
1258 table, uint32(creatureMovement.Flight), id);
1259 creatureMovement.Flight = CreatureFlightMovementType::None;
1260 }
1261
1262 if (creatureMovement.Chase >= CreatureChaseMovementType::Max)
1263 {
1264 TC_LOG_ERROR("sql.sql", "`{}`.`Chase` wrong value ({}) for Id {}, setting to Run.",
1265 table, uint32(creatureMovement.Chase), id);
1266 creatureMovement.Chase = CreatureChaseMovementType::Run;
1267 }
1268
1269 if (creatureMovement.Random >= CreatureRandomMovementType::Max)
1270 {
1271 TC_LOG_ERROR("sql.sql", "`{}`.`Random` wrong value ({}) for Id {}, setting to Walk.",
1272 table, uint32(creatureMovement.Random), id);
1273 creatureMovement.Random = CreatureRandomMovementType::Walk;
1274 }
1275}
1276
1278{
1279 uint32 oldMSTime = getMSTime();
1280
1281 // 0 1 2 3 4 5 6 7 8 9 10
1282 QueryResult result = WorldDatabase.Query("SELECT guid, path_id, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, visibilityDistanceType, auras FROM creature_addon");
1283
1284 if (!result)
1285 {
1286 TC_LOG_INFO("server.loading", ">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
1287 return;
1288 }
1289
1290 uint32 count = 0;
1291 do
1292 {
1293 Field* fields = result->Fetch();
1294
1295 ObjectGuid::LowType guid = fields[0].GetUInt32();
1296
1297 CreatureData const* creData = GetCreatureData(guid);
1298 if (!creData)
1299 {
1300 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_addon`", guid);
1301 continue;
1302 }
1303
1304 CreatureAddon& creatureAddon = _creatureAddonStore[guid];
1305
1306 creatureAddon.path_id = fields[1].GetUInt32();
1307 if (creData->movementType == WAYPOINT_MOTION_TYPE && !creatureAddon.path_id)
1308 {
1309 const_cast<CreatureData*>(creData)->movementType = IDLE_MOTION_TYPE;
1310 TC_LOG_ERROR("sql.sql", "Creature (GUID {}) has movement type set to WAYPOINT_MOTION_TYPE but no path assigned", guid);
1311 }
1312
1313 creatureAddon.mount = fields[2].GetUInt32();
1314 creatureAddon.standState = fields[3].GetUInt8();
1315 creatureAddon.animTier = fields[4].GetUInt8();
1316 creatureAddon.visFlags = fields[5].GetUInt8();
1317 creatureAddon.sheathState = fields[6].GetUInt8();
1318 creatureAddon.pvpFlags = fields[7].GetUInt8();
1319 creatureAddon.emote = fields[8].GetUInt32();
1320 creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[9].GetUInt8());
1321
1322 for (std::string_view aura : Trinity::Tokenize(fields[10].GetStringView(), ' ', false))
1323 {
1324 SpellInfo const* spellInfo = nullptr;
1325 if (Optional<uint32> spellId = Trinity::StringTo<uint32>(aura))
1326 spellInfo = sSpellMgr->GetSpellInfo(*spellId);
1327
1328 if (!spellInfo)
1329 {
1330 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has wrong spell '{}' defined in `auras` field in `creature_addon`.", guid, aura);
1331 continue;
1332 }
1333
1334 if (spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE))
1335 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has SPELL_AURA_CONTROL_VEHICLE aura {} defined in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1336
1337 if (std::find(creatureAddon.auras.begin(), creatureAddon.auras.end(), spellInfo->Id) != creatureAddon.auras.end())
1338 {
1339 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has duplicate aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1340 continue;
1341 }
1342
1343 if (spellInfo->GetDuration() > 0)
1344 {
1345 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has temporary aura (spell {}) in `auras` field in `creature_addon`.", guid, spellInfo->Id);
1346 continue;
1347 }
1348
1349 creatureAddon.auras.push_back(spellInfo->Id);
1350 }
1351
1352 if (creatureAddon.mount)
1353 {
1354 if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
1355 {
1356 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid displayInfoId ({}) for mount defined in `creature_addon`", guid, creatureAddon.mount);
1357 creatureAddon.mount = 0;
1358 }
1359 }
1360
1361 if (creatureAddon.standState >= MAX_UNIT_STAND_STATE)
1362 {
1363 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid unit stand state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.standState);
1364 creatureAddon.standState = 0;
1365 }
1366
1367 if (AnimTier(creatureAddon.animTier) >= AnimTier::Max)
1368 {
1369 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid animation tier ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.animTier);
1370 creatureAddon.animTier = 0;
1371 }
1372
1373 if (creatureAddon.sheathState >= MAX_SHEATH_STATE)
1374 {
1375 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid sheath state ({}) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.sheathState);
1376 creatureAddon.sheathState = 0;
1377 }
1378
1379 // PvPFlags don't need any checking for the time being since they cover the entire range of a byte
1380
1381 if (!sEmotesStore.LookupEntry(creatureAddon.emote))
1382 {
1383 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid emote ({}) defined in `creature_addon`.", guid, creatureAddon.emote);
1384 creatureAddon.emote = 0;
1385 }
1386
1388 {
1389 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) has invalid visibilityDistanceType ({}) defined in `creature_addon`.",
1390 guid, AsUnderlyingType(creatureAddon.visibilityDistanceType));
1392 }
1393
1394 ++count;
1395 }
1396 while (result->NextRow());
1397
1398 TC_LOG_INFO("server.loading", ">> Loaded {} creature addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1399}
1400
1402{
1403 uint32 oldMSTime = getMSTime();
1404
1405 // 0 1 2 3 4 5 6
1406 QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue FROM gameobject_addon");
1407
1408 if (!result)
1409 {
1410 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject addon definitions. DB table `gameobject_addon` is empty.");
1411 return;
1412 }
1413
1414 uint32 count = 0;
1415 do
1416 {
1417 Field* fields = result->Fetch();
1418
1419 ObjectGuid::LowType guid = fields[0].GetUInt32();
1420
1421 GameObjectData const* goData = GetGameObjectData(guid);
1422 if (!goData)
1423 {
1424 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) does not exist but has a record in `gameobject_addon`", guid);
1425 continue;
1426 }
1427
1428 GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid];
1429 gameObjectAddon.ParentRotation = QuaternionData(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
1430 gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8());
1431 gameObjectAddon.InvisibilityValue = fields[6].GetUInt32();
1432
1433 if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES)
1434 {
1435 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid);
1436 gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL;
1437 gameObjectAddon.InvisibilityValue = 0;
1438 }
1439
1440 if (gameObjectAddon.invisibilityType && !gameObjectAddon.InvisibilityValue)
1441 {
1442 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has InvisibilityType set but has no InvisibilityValue in `gameobject_addon`, set to 1", guid);
1443 gameObjectAddon.InvisibilityValue = 1;
1444 }
1445
1446 if (!gameObjectAddon.ParentRotation.isUnit())
1447 {
1448 TC_LOG_ERROR("sql.sql", "GameObject (GUID: {}) has invalid parent rotation in `gameobject_addon`, set to default", guid);
1449 gameObjectAddon.ParentRotation = QuaternionData();
1450 }
1451
1452 ++count;
1453 }
1454 while (result->NextRow());
1455
1456 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1457}
1458
1460{
1461 GameObjectAddonContainer::const_iterator itr = _gameObjectAddonStore.find(lowguid);
1462 if (itr != _gameObjectAddonStore.end())
1463 return &(itr->second);
1464
1465 return nullptr;
1466}
1467
1469{
1470 CreatureAddonContainer::const_iterator itr = _creatureAddonStore.find(lowguid);
1471 if (itr != _creatureAddonStore.end())
1472 return &(itr->second);
1473
1474 return nullptr;
1475}
1476
1478{
1479 CreatureTemplateAddonContainer::const_iterator itr = _creatureTemplateAddonStore.find(entry);
1480 if (itr != _creatureTemplateAddonStore.end())
1481 return &(itr->second);
1482
1483 return nullptr;
1484}
1485
1490
1492{
1493 EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry);
1494 if (itr == _equipmentInfoStore.end())
1495 return nullptr;
1496
1497 if (itr->second.empty())
1498 return nullptr;
1499
1500 if (id == -1) // select a random element
1501 {
1502 EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin();
1503 std::advance(ritr, urand(0u, itr->second.size() - 1));
1504 id = std::distance(itr->second.begin(), ritr) + 1;
1505 return &ritr->second;
1506 }
1507 else
1508 {
1509 EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id);
1510 if (itr2 != itr->second.end())
1511 return &itr2->second;
1512 }
1513
1514 return nullptr;
1515}
1516
1518{
1519 uint32 oldMSTime = getMSTime();
1520
1521 // 0 1 2 3 4
1522 QueryResult result = WorldDatabase.Query("SELECT CreatureID, ID, ItemID1, ItemID2, ItemID3 FROM creature_equip_template");
1523
1524 if (!result)
1525 {
1526 TC_LOG_INFO("server.loading", ">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
1527 return;
1528 }
1529
1530 uint32 count = 0;
1531 do
1532 {
1533 Field* fields = result->Fetch();
1534
1535 uint32 entry = fields[0].GetUInt32();
1536
1537 if (!sObjectMgr->GetCreatureTemplate(entry))
1538 {
1539 TC_LOG_ERROR("sql.sql", "Creature template (Entry: {}) does not exist but has a record in `creature_equip_template`", entry);
1540 continue;
1541 }
1542
1543 uint8 id = fields[1].GetUInt8();
1544 if (!id)
1545 {
1546 TC_LOG_ERROR("sql.sql", "Creature equipment template with id 0 found for creature {}, skipped.", entry);
1547 continue;
1548 }
1549
1550 EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id];
1551
1552 equipmentInfo.ItemEntry[0] = fields[2].GetUInt32();
1553 equipmentInfo.ItemEntry[1] = fields[3].GetUInt32();
1554 equipmentInfo.ItemEntry[2] = fields[4].GetUInt32();
1555
1556 for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
1557 {
1558 if (!equipmentInfo.ItemEntry[i])
1559 continue;
1560
1561 ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]);
1562
1563 if (!dbcItem)
1564 {
1565 TC_LOG_ERROR("sql.sql", "Unknown item (entry={}) in creature_equip_template.itemEntry{} for entry = {} and id={}, forced to 0.",
1566 equipmentInfo.ItemEntry[i], i+1, entry, id);
1567 equipmentInfo.ItemEntry[i] = 0;
1568 continue;
1569 }
1570
1571 if (dbcItem->InventoryType != INVTYPE_WEAPON &&
1572 dbcItem->InventoryType != INVTYPE_SHIELD &&
1573 dbcItem->InventoryType != INVTYPE_RANGED &&
1574 dbcItem->InventoryType != INVTYPE_2HWEAPON &&
1577 dbcItem->InventoryType != INVTYPE_HOLDABLE &&
1578 dbcItem->InventoryType != INVTYPE_THROWN &&
1580 {
1581 TC_LOG_ERROR("sql.sql", "Item (entry={}) in creature_equip_template.itemEntry{} for entry = {} and id = {} is not equipable in a hand, forced to 0.",
1582 equipmentInfo.ItemEntry[i], i+1, entry, id);
1583 equipmentInfo.ItemEntry[i] = 0;
1584 }
1585 }
1586
1587 ++count;
1588 }
1589 while (result->NextRow());
1590
1591 TC_LOG_INFO("server.loading", ">> Loaded {} equipment templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1592}
1593
1595{
1596 uint32 oldMSTime = getMSTime();
1597
1599
1600 // Load the data from creature_movement_override and if NULL fallback to creature_template_movement
1601 QueryResult result = WorldDatabase.Query(
1602 "SELECT cmo.SpawnId,"
1603 "COALESCE(cmo.Ground, ctm.Ground),"
1604 "COALESCE(cmo.Swim, ctm.Swim),"
1605 "COALESCE(cmo.Flight, ctm.Flight),"
1606 "COALESCE(cmo.Rooted, ctm.Rooted),"
1607 "COALESCE(cmo.Chase, ctm.Chase),"
1608 "COALESCE(cmo.Random, ctm.Random),"
1609 "COALESCE(cmo.InteractionPauseTimer, ctm.InteractionPauseTimer) "
1610 "FROM creature_movement_override AS cmo "
1611 "LEFT JOIN creature AS c ON c.guid = cmo.SpawnId "
1612 "LEFT JOIN creature_template_movement AS ctm ON ctm.CreatureId = c.id");
1613
1614 if (!result)
1615 {
1616 TC_LOG_INFO("server.loading", ">> Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
1617 return;
1618 }
1619
1620 do
1621 {
1622 Field* fields = result->Fetch();
1623 ObjectGuid::LowType spawnId = fields[0].GetUInt32();
1624 if (!GetCreatureData(spawnId))
1625 {
1626 TC_LOG_ERROR("sql.sql", "Creature (GUID: {}) does not exist but has a record in `creature_movement_override`", spawnId);
1627 continue;
1628 }
1629
1631 if (!fields[1].IsNull())
1632 movement.Ground = static_cast<CreatureGroundMovementType>(fields[1].GetUInt8());
1633 if (!fields[2].IsNull())
1634 movement.Swim = fields[2].GetBool();
1635 if (!fields[3].IsNull())
1636 movement.Flight = static_cast<CreatureFlightMovementType>(fields[3].GetUInt8());
1637 if (!fields[4].IsNull())
1638 movement.Rooted = fields[4].GetBool();
1639 if (!fields[5].IsNull())
1640 movement.Chase = static_cast<CreatureChaseMovementType>(fields[5].GetUInt8());
1641 if (!fields[6].IsNull())
1642 movement.Random = static_cast<CreatureRandomMovementType>(fields[6].GetUInt8());
1643 if (!fields[7].IsNull())
1644 movement.InteractionPauseTimer = fields[7].GetUInt32();
1645
1646 CheckCreatureMovement("creature_movement_override", spawnId, movement);
1647 }
1648 while (result->NextRow());
1649
1650 TC_LOG_INFO("server.loading", ">> Loaded {} movement overrides in {} ms", _creatureMovementOverrides.size(), GetMSTimeDiffToNow(oldMSTime));
1651}
1652
1654{
1655 CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId);
1656 if (itr != _creatureModelStore.end())
1657 return &(itr->second);
1658
1659 return nullptr;
1660}
1661
1662uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/)
1663{
1664 // Load creature model (display id)
1665 if (data && data->displayid)
1666 return data->displayid;
1667
1669 return cinfo->GetRandomValidModelId();
1670
1671 // Triggers by default receive the invisible model
1672 return cinfo->GetFirstInvisibleModel();
1673}
1674
1675void ObjectMgr::ChooseCreatureFlags(CreatureTemplate const* cinfo, uint32* npcflag, uint32* unit_flags, uint32* dynamicflags, CreatureData const* data /*= nullptr*/)
1676{
1677#define ChooseCreatureFlagSource(field) ((data && data->field) ? data->field : cinfo->field)
1678
1679 if (npcflag)
1680 *npcflag = ChooseCreatureFlagSource(npcflag);
1681
1682 if (unit_flags)
1683 *unit_flags = ChooseCreatureFlagSource(unit_flags);
1684
1685 if (dynamicflags)
1686 *dynamicflags = ChooseCreatureFlagSource(dynamicflags);
1687
1688#undef ChooseCreatureFlagSource
1689}
1690
1692{
1693 CreatureModelInfo const* modelInfo = GetCreatureModelInfo(*displayID);
1694 if (!modelInfo)
1695 return nullptr;
1696
1697 // If a model for another gender exists, 50% chance to use it
1698 if (modelInfo->displayId_other_gender != 0 && urand(0, 1) == 0)
1699 {
1700 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->displayId_other_gender);
1701 if (!minfo_tmp)
1702 TC_LOG_ERROR("sql.sql", "Model (Entry: {}) has modelid_other_gender {} not found in table `creature_model_info`. ", *displayID, modelInfo->displayId_other_gender);
1703 else
1704 {
1705 // DisplayID changed
1706 *displayID = modelInfo->displayId_other_gender;
1707 return minfo_tmp;
1708 }
1709 }
1710
1711 return modelInfo;
1712}
1713
1715{
1716 uint32 oldMSTime = getMSTime();
1717 // 0 1 2 3 4
1718 QueryResult result = WorldDatabase.Query("SELECT DisplayID, BoundingRadius, CombatReach, DisplayID_Other_Gender, Gender FROM creature_model_info");
1719
1720 if (!result)
1721 {
1722 TC_LOG_INFO("server.loading", ">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
1723 return;
1724 }
1725
1726 _creatureModelStore.rehash(result->GetRowCount());
1727 uint32 count = 0;
1728
1729 do
1730 {
1731 Field* fields = result->Fetch();
1732
1733 uint32 displayId = fields[0].GetUInt32();
1734
1735 CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(displayId);
1736 if (!creatureDisplay)
1737 {
1738 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has a non-existent DisplayID (ID: {}). Skipped.", displayId);
1739 continue;
1740 }
1741
1742 CreatureModelInfo& modelInfo = _creatureModelStore[displayId];
1743
1744 modelInfo.bounding_radius = fields[1].GetFloat();
1745 modelInfo.combat_reach = fields[2].GetFloat();
1746 modelInfo.displayId_other_gender = fields[3].GetUInt32();
1747 modelInfo.gender = fields[4].GetInt8();
1748 modelInfo.is_trigger = false;
1749
1750 // Checks
1751
1752 if (modelInfo.gender > GENDER_NONE)
1753 {
1754 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has wrong gender ({}) for display id ({}).", uint32(modelInfo.gender), displayId);
1755 modelInfo.gender = GENDER_MALE;
1756 }
1757
1758 if (modelInfo.displayId_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.displayId_other_gender))
1759 {
1760 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);
1761 modelInfo.displayId_other_gender = 0;
1762 }
1763
1764 if (modelInfo.combat_reach < 0.1f)
1766
1767 if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelID))
1768 modelInfo.is_trigger = strstr(modelData->ModelName, "InvisibleStalker") != nullptr;
1769
1770 ++count;
1771 }
1772 while (result->NextRow());
1773
1774 TC_LOG_INFO("server.loading", ">> Loaded {} creature model based info in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1775}
1776
1778{
1779 uint32 oldMSTime = getMSTime();
1780
1781 QueryResult result = WorldDatabase.Query("SELECT TotemSlot, RaceId, DisplayId from player_totem_model");
1782
1783 if (!result)
1784 {
1785 TC_LOG_INFO("server.loading", ">> Loaded 0 player totem model records. DB table `player_totem_model` is empty.");
1786 return;
1787 }
1788
1789 uint32 count = 0;
1790 do
1791 {
1792 Field* fields = result->Fetch();
1793
1794 SummonSlot totemSlot = SummonSlot(fields[0].GetUInt8());
1795 uint8 race = fields[1].GetUInt8();
1796 uint32 displayId = fields[2].GetUInt32();
1797
1798 if (totemSlot < SUMMON_SLOT_TOTEM_FIRE || totemSlot >= MAX_TOTEM_SLOT)
1799 {
1800 TC_LOG_ERROR("sql.sql", "Wrong TotemSlot {} in `player_totem_model` table, skipped.", totemSlot);
1801 continue;
1802 }
1803
1804 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race);
1805 if (!raceEntry)
1806 {
1807 TC_LOG_ERROR("sql.sql", "Race {} defined in `player_totem_model` does not exists, skipped.", uint32(race));
1808 continue;
1809 }
1810
1811 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(displayId);
1812 if (!displayEntry)
1813 {
1814 TC_LOG_ERROR("sql.sql", "TotemSlot: {} defined in `player_totem_model` has non-existing model ({}), skipped.", totemSlot, displayId);
1815 continue;
1816 }
1817
1818 _playerTotemModel[std::make_pair(totemSlot, Races(race))] = displayId;
1819 ++count;
1820 }
1821 while (result->NextRow());
1822
1823 TC_LOG_INFO("server.loading", ">> Loaded {} player totem model records in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1824}
1825
1827{
1828 auto itr = _playerTotemModel.find(std::make_pair(totemSlot, race));
1829 if (itr != _playerTotemModel.end())
1830 return itr->second;
1831 return 0;
1832}
1833
1835{
1836 uint32 oldMSTime = getMSTime();
1837
1838 _linkedRespawnStore.clear();
1839 // 0 1 2
1840 QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
1841
1842 if (!result)
1843 {
1844 TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
1845 return;
1846 }
1847
1848 do
1849 {
1850 Field* fields = result->Fetch();
1851
1852 ObjectGuid::LowType guidLow = fields[0].GetUInt32();
1853 ObjectGuid::LowType linkedGuidLow = fields[1].GetUInt32();
1854 uint8 linkType = fields[2].GetUInt8();
1855
1856 ObjectGuid guid, linkedGuid;
1857 bool error = false;
1858 switch (linkType)
1859 {
1861 {
1862 CreatureData const* slave = GetCreatureData(guidLow);
1863 if (!slave)
1864 {
1865 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1866 error = true;
1867 break;
1868 }
1869
1870 CreatureData const* master = GetCreatureData(linkedGuidLow);
1871 if (!master)
1872 {
1873 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1874 error = true;
1875 break;
1876 }
1877
1878 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1879 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1880 {
1881 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1882 error = true;
1883 break;
1884 }
1885
1886 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1887 {
1888 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1889 error = true;
1890 break;
1891 }
1892
1893 guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
1894 linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
1895 break;
1896 }
1898 {
1899 CreatureData const* slave = GetCreatureData(guidLow);
1900 if (!slave)
1901 {
1902 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1903 error = true;
1904 break;
1905 }
1906
1907 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1908 if (!master)
1909 {
1910 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1911 error = true;
1912 break;
1913 }
1914
1915 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1916 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1917 {
1918 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1919 error = true;
1920 break;
1921 }
1922
1923 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1924 {
1925 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1926 error = true;
1927 break;
1928 }
1929
1930 guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
1931 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->id, linkedGuidLow);
1932 break;
1933 }
1935 {
1936 GameObjectData const* slave = GetGameObjectData(guidLow);
1937 if (!slave)
1938 {
1939 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1940 error = true;
1941 break;
1942 }
1943
1944 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1945 if (!master)
1946 {
1947 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1948 error = true;
1949 break;
1950 }
1951
1952 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1953 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1954 {
1955 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1956 error = true;
1957 break;
1958 }
1959
1960 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1961 {
1962 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1963 error = true;
1964 break;
1965 }
1966
1967 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->id, guidLow);
1968 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->id, linkedGuidLow);
1969 break;
1970 }
1972 {
1973 GameObjectData const* slave = GetGameObjectData(guidLow);
1974 if (!slave)
1975 {
1976 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1977 error = true;
1978 break;
1979 }
1980
1981 CreatureData const* master = GetCreatureData(linkedGuidLow);
1982 if (!master)
1983 {
1984 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1985 error = true;
1986 break;
1987 }
1988
1989 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1990 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1991 {
1992 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1993 error = true;
1994 break;
1995 }
1996
1997 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1998 {
1999 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
2000 error = true;
2001 break;
2002 }
2003
2004 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->id, guidLow);
2005 linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
2006 break;
2007 }
2008 }
2009
2010 if (!error)
2011 _linkedRespawnStore[guid] = linkedGuid;
2012 }
2013 while (result->NextRow());
2014
2015 TC_LOG_INFO("server.loading", ">> Loaded {} linked respawns in {} ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
2016}
2017
2019{
2020 if (!guidLow)
2021 return false;
2022
2023 CreatureData const* master = GetCreatureData(guidLow);
2024 ASSERT(master);
2025 ObjectGuid guid = ObjectGuid::Create<HighGuid::Unit>(master->id, guidLow);
2026
2027 if (!linkedGuidLow) // we're removing the linking
2028 {
2029 _linkedRespawnStore.erase(guid);
2031 stmt->setUInt32(0, guidLow);
2033 WorldDatabase.Execute(stmt);
2034 return true;
2035 }
2036
2037 CreatureData const* slave = GetCreatureData(linkedGuidLow);
2038 if (!slave)
2039 {
2040 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to non-existent creature '{}'.", guidLow, linkedGuidLow);
2041 return false;
2042 }
2043
2044 MapEntry const* map = sMapStore.LookupEntry(master->mapId);
2045 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
2046 {
2047 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to '{}' on an unpermitted map.", guidLow, linkedGuidLow);
2048 return false;
2049 }
2050
2051 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
2052 {
2053 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
2054 return false;
2055 }
2056
2057 ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id, linkedGuidLow);
2058
2059 _linkedRespawnStore[guid] = linkedGuid;
2061 stmt->setUInt32(0, guidLow);
2062 stmt->setUInt32(1, linkedGuidLow);
2064 WorldDatabase.Execute(stmt);
2065 return true;
2066}
2067
2069{
2070 uint32 oldMSTime = getMSTime();
2071
2072 _tempSummonDataStore.clear(); // needed for reload case
2073
2074 // 0 1 2 3 4 5 6 7 8 9
2075 QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
2076
2077 if (!result)
2078 {
2079 TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
2080 return;
2081 }
2082
2083 uint32 count = 0;
2084 do
2085 {
2086 Field* fields = result->Fetch();
2087
2088 uint32 summonerId = fields[0].GetUInt32();
2089 SummonerType summonerType = SummonerType(fields[1].GetUInt8());
2090 uint8 group = fields[2].GetUInt8();
2091
2092 switch (summonerType)
2093 {
2095 if (!GetCreatureTemplate(summonerId))
2096 {
2097 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for creature summoner type, skipped.", summonerId);
2098 continue;
2099 }
2100 break;
2102 if (!GetGameObjectTemplate(summonerId))
2103 {
2104 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for gameobject summoner type, skipped.", summonerId);
2105 continue;
2106 }
2107 break;
2108 case SUMMONER_TYPE_MAP:
2109 if (!sMapStore.LookupEntry(summonerId))
2110 {
2111 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for map summoner type, skipped.", summonerId);
2112 continue;
2113 }
2114 break;
2115 default:
2116 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type {} for summoner {}, skipped.", summonerType, summonerId);
2117 continue;
2118 }
2119
2120 TempSummonData data;
2121 data.entry = fields[3].GetUInt32();
2122
2123 if (!GetCreatureTemplate(data.entry))
2124 {
2125 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);
2126 continue;
2127 }
2128
2129 float posX = fields[4].GetFloat();
2130 float posY = fields[5].GetFloat();
2131 float posZ = fields[6].GetFloat();
2132 float orientation = fields[7].GetFloat();
2133
2134 data.pos.Relocate(posX, posY, posZ, orientation);
2135
2136 data.type = TempSummonType(fields[8].GetUInt8());
2137
2139 {
2140 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);
2141 continue;
2142 }
2143
2144 data.time = fields[9].GetUInt32();
2145
2146 TempSummonGroupKey key(summonerId, summonerType, group);
2147 _tempSummonDataStore[key].push_back(data);
2148
2149 ++count;
2150
2151 } while (result->NextRow());
2152
2153 TC_LOG_INFO("server.loading", ">> Loaded {} temp summons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
2154}
2155
2157{
2158 uint32 oldMSTime = getMSTime();
2159
2160 // 0 1 2 3 4 5 6 7 8 9 10
2161 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, "
2162 // 11 12 13 14 15 16 17 18 19 20 21
2163 "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
2164 // 22 23
2165 "creature.ScriptName, creature.StringId "
2166 "FROM creature "
2167 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
2168 "LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId");
2169
2170 if (!result)
2171 {
2172 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
2173 return;
2174 }
2175
2176 // Build single time for check spawnmask
2177 std::map<uint32, uint32> spawnMasks;
2178 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
2179 if (sMapStore.LookupEntry(i))
2180 for (uint8 k = 0; k < MAX_DIFFICULTY; ++k)
2182 spawnMasks[i] |= (1 << k);
2183
2184 _creatureDataStore.rehash(result->GetRowCount());
2185
2186 do
2187 {
2188 Field* fields = result->Fetch();
2189
2190 ObjectGuid::LowType guid = fields[0].GetUInt32();
2191 uint32 entry = fields[1].GetUInt32();
2192
2193 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2194 if (!cInfo)
2195 {
2196 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) with non existing creature entry {}, skipped.", guid, entry);
2197 continue;
2198 }
2199
2200 CreatureData& data = _creatureDataStore[guid];
2201 data.spawnId = guid;
2202 data.id = entry;
2203 data.mapId = fields[2].GetUInt16();
2204 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2205 data.displayid = fields[7].GetUInt32();
2206 data.equipmentId = fields[8].GetInt8();
2207 data.spawntimesecs = fields[9].GetUInt32();
2208 data.wander_distance = fields[10].GetFloat();
2209 data.currentwaypoint= fields[11].GetUInt32();
2210 data.curhealth = fields[12].GetUInt32();
2211 data.curmana = fields[13].GetUInt32();
2212 data.movementType = fields[14].GetUInt8();
2213 data.spawnMask = fields[15].GetUInt8();
2214 data.phaseMask = fields[16].GetUInt32();
2215 int16 gameEvent = fields[17].GetInt8();
2216 uint32 PoolId = fields[18].GetUInt32();
2217 data.npcflag = fields[19].GetUInt32();
2218 data.unit_flags = fields[20].GetUInt32();
2219 data.dynamicflags = fields[21].GetUInt32();
2220 data.scriptId = GetScriptId(fields[22].GetString());
2221 data.StringId = fields[23].GetString();
2223
2224 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2225 if (!mapEntry)
2226 {
2227 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that spawned at nonexistent map (Id: {}), skipped.", guid, data.mapId);
2228 continue;
2229 }
2230
2231 // Skip spawnMask check for transport maps
2232 if (!IsTransportMap(data.mapId))
2233 {
2234 if (data.spawnMask & ~spawnMasks[data.mapId])
2235 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that have wrong spawn mask {} including unsupported difficulty modes for map (Id: {}).", guid, data.spawnMask, data.mapId);
2236 }
2237 else
2238 data.spawnGroupData = GetLegacySpawnGroup(); // force compatibility group for transport spawns
2239
2240 bool ok = true;
2241 for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
2242 {
2243 if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
2244 {
2245 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is listed as difficulty {} template (entry: {}) in `creature_template`, skipped.",
2246 guid, diff + 1, data.id);
2247 ok = false;
2248 }
2249 }
2250 if (!ok)
2251 continue;
2252
2253 // -1 random, 0 no equipment
2254 if (data.equipmentId != 0)
2255 {
2256 if (!GetEquipmentInfo(data.id, data.equipmentId))
2257 {
2258 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);
2259 data.equipmentId = 0;
2260 }
2261 }
2262
2264 {
2265 if (!mapEntry->IsDungeon())
2266 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);
2267 }
2268
2269 if (data.movementType >= MAX_DB_MOTION_TYPE)
2270 {
2271 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);
2273 }
2274
2275 if (data.wander_distance < 0.0f)
2276 {
2277 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `wander_distance`< 0, set to 0.", guid, data.id);
2278 data.wander_distance = 0.0f;
2279 }
2280 else if (data.movementType == RANDOM_MOTION_TYPE)
2281 {
2282 if (data.wander_distance == 0.0f)
2283 {
2284 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);
2286 }
2287 }
2288 else if (data.movementType == IDLE_MOTION_TYPE)
2289 {
2290 if (data.wander_distance != 0.0f)
2291 {
2292 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);
2293 data.wander_distance = 0.0f;
2294 }
2295 }
2296
2297 if (data.phaseMask == 0)
2298 {
2299 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
2300 data.phaseMask = 1;
2301 }
2302
2303 if (uint32 disallowedUnitFlags = (data.unit_flags & ~UNIT_FLAG_ALLOWED))
2304 {
2305 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags);
2307 }
2308
2309 if (data.dynamicflags)
2310 {
2311 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `dynamicflags` > 0. Ignored and set to 0.", guid, data.id);
2312 data.dynamicflags = 0;
2313 }
2314
2316 {
2317 uint32 zoneId = 0;
2318 uint32 areaId = 0;
2319 sMapMgr->GetZoneAndAreaId(data.phaseMask, zoneId, areaId, data.mapId, data.spawnPoint);
2320
2322
2323 stmt->setUInt32(0, zoneId);
2324 stmt->setUInt32(1, areaId);
2325 stmt->setUInt64(2, guid);
2326
2327 WorldDatabase.Execute(stmt);
2328 }
2329
2330 // Add to grid if not managed by the game event or pool system
2331 if (gameEvent == 0 && PoolId == 0)
2332 AddCreatureToGrid(guid, &data);
2333 }
2334 while (result->NextRow());
2335
2336 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in {} ms", _creatureDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2337}
2338
2340{
2342 return Trinity::Containers::MapGetValuePtr(*mapGuids, cell_id);
2343
2344 return nullptr;
2345}
2346
2351
2353{
2354 uint8 mask = data->spawnMask;
2355 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2356 {
2357 if (mask & 1)
2358 {
2360 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2361 cell_guids.creatures.insert(guid);
2362 }
2363 }
2364}
2365
2367{
2368 uint8 mask = data->spawnMask;
2369 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2370 {
2371 if (mask & 1)
2372 {
2374 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2375 cell_guids.creatures.erase(guid);
2376 }
2377 }
2378}
2379
2380ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
2381{
2382 GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
2383 if (!goinfo)
2384 return 0;
2385
2386 Map* map = sMapMgr->CreateBaseMap(mapId);
2387 if (!map)
2388 return 0;
2389
2391
2393 data.spawnId = spawnId;
2394 data.id = entry;
2395 data.mapId = mapId;
2396 data.spawnPoint.Relocate(pos);
2397 data.rotation = rot;
2398 data.spawntimesecs = spawntimedelay;
2399 data.animprogress = 100;
2400 data.spawnMask = 1;
2401 data.goState = GO_STATE_READY;
2403 data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
2404 data.dbData = false;
2406
2407 AddGameobjectToGrid(spawnId, &data);
2408
2409 // Spawn if necessary (loaded grids only)
2410 // We use spawn coords to spawn
2411 if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint))
2412 {
2413 GameObject* go = new GameObject;
2414 if (!go->LoadFromDB(spawnId, map, true))
2415 {
2416 TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry {} to map", entry);
2417 delete go;
2418 return 0;
2419 }
2420 }
2421
2422 TC_LOG_DEBUG("maps", "AddGameObjectData: dbguid {} entry {} map {} pos {}", spawnId, entry, mapId, data.spawnPoint.ToString());
2423
2424 return spawnId;
2425}
2426
2427ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Position const& pos, uint32 spawntimedelay /*= 0*/)
2428{
2429 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2430 if (!cInfo)
2431 return 0;
2432
2433 uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
2434 CreatureBaseStats const* stats = GetCreatureBaseStats(level, cInfo->unit_class);
2435 Map* map = sMapMgr->CreateBaseMap(mapId);
2436 if (!map)
2437 return 0;
2438
2440 CreatureData& data = NewOrExistCreatureData(spawnId);
2441 data.spawnId = spawnId;
2442 data.id = entry;
2443 data.mapId = mapId;
2444 data.spawnPoint.Relocate(pos);
2445 data.displayid = 0;
2446 data.equipmentId = 0;
2447 data.spawntimesecs = spawntimedelay;
2448 data.wander_distance = 0;
2449 data.currentwaypoint = 0;
2450 data.curhealth = stats->GenerateHealth(cInfo);
2451 data.curmana = stats->GenerateMana(cInfo);
2452 data.movementType = cInfo->MovementType;
2453 data.spawnMask = 1;
2455 data.dbData = false;
2456 data.npcflag = cInfo->npcflag;
2457 data.unit_flags = cInfo->unit_flags;
2458 data.dynamicflags = cInfo->dynamicflags;
2460
2461 AddCreatureToGrid(spawnId, &data);
2462
2463 // We use spawn coords to spawn
2464 if (!map->Instanceable() && !map->IsRemovalGrid(data.spawnPoint))
2465 {
2466 Creature* creature = new Creature();
2467 if (!creature->LoadFromDB(spawnId, map, true, true))
2468 {
2469 TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry {} to map", entry);
2470 delete creature;
2471 return 0;
2472 }
2473 }
2474
2475 return spawnId;
2476}
2477
2479{
2480 uint32 oldMSTime = getMSTime();
2481
2482 // 0 1 2 3 4 5 6
2483 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
2484 // 7 8 9 10 11 12 13 14 15 16 17
2485 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, eventEntry, poolSpawnId, "
2486 // 18 19
2487 "ScriptName, StringId "
2488 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
2489 "LEFT OUTER JOIN pool_members ON pool_members.type = 1 AND gameobject.guid = pool_members.spawnId");
2490
2491 if (!result)
2492 {
2493 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
2494 return;
2495 }
2496
2497 // build single time for check spawnmask
2498 std::map<uint32, uint32> spawnMasks;
2499 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
2500 if (sMapStore.LookupEntry(i))
2501 for (uint8 k = 0; k < MAX_DIFFICULTY; ++k)
2503 spawnMasks[i] |= (1 << k);
2504
2505 _gameObjectDataStore.rehash(result->GetRowCount());
2506
2507 do
2508 {
2509 Field* fields = result->Fetch();
2510
2511 ObjectGuid::LowType guid = fields[0].GetUInt32();
2512 uint32 entry = fields[1].GetUInt32();
2513
2514 GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
2515 if (!gInfo)
2516 {
2517 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {}) with non existing gameobject entry {}, skipped.", guid, entry);
2518 continue;
2519 }
2520
2521 if (!gInfo->displayId)
2522 {
2523 switch (gInfo->type)
2524 {
2527 break;
2528 default:
2529 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) doesn't have a displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2530 break;
2531 }
2532 }
2533
2534 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
2535 {
2536 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) has an invalid displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2537 continue;
2538 }
2539
2541
2542 data.spawnId = guid;
2543 data.id = entry;
2544 data.mapId = fields[2].GetUInt16();
2545 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2546 data.rotation.x = fields[7].GetFloat();
2547 data.rotation.y = fields[8].GetFloat();
2548 data.rotation.z = fields[9].GetFloat();
2549 data.rotation.w = fields[10].GetFloat();
2550 data.spawntimesecs = fields[11].GetInt32();
2552
2553 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2554 if (!mapEntry)
2555 {
2556 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) spawned on a non-existed map (Id: {}), skip", guid, data.id, data.mapId);
2557 continue;
2558 }
2559
2560 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
2561 {
2562 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);
2563 }
2564
2565 data.animprogress = fields[12].GetUInt8();
2566 data.artKit = 0;
2567
2568 uint32 go_state = fields[13].GetUInt8();
2569 if (go_state >= MAX_GO_STATE)
2570 {
2571 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid `state` ({}) value, skip", guid, data.id, go_state);
2572 continue;
2573 }
2574 data.goState = GOState(go_state);
2575
2576 data.spawnMask = fields[14].GetUInt8();
2577
2578 if (!IsTransportMap(data.mapId))
2579 {
2580 if (data.spawnMask & ~spawnMasks[data.mapId])
2581 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) that has wrong spawn mask {} including unsupported difficulty modes for map (Id: {}), skip", guid, data.id, data.spawnMask, data.mapId);
2582 }
2583 else
2584 data.spawnGroupData = GetLegacySpawnGroup(); // force compatibility group for transport spawns
2585
2586 data.phaseMask = fields[15].GetUInt32();
2587 int16 gameEvent = fields[16].GetInt8();
2588 uint32 PoolId = fields[17].GetUInt32();
2589
2590 data.scriptId = GetScriptId(fields[18].GetString());
2591 data.StringId = fields[19].GetString();
2592
2593 if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
2594 {
2595 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationX ({}) value, skip", guid, data.id, data.rotation.x);
2596 continue;
2597 }
2598
2599 if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
2600 {
2601 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationY ({}) value, skip", guid, data.id, data.rotation.y);
2602 continue;
2603 }
2604
2605 if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
2606 {
2607 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationZ ({}) value, skip", guid, data.id, data.rotation.z);
2608 continue;
2609 }
2610
2611 if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
2612 {
2613 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationW ({}) value, skip", guid, data.id, data.rotation.w);
2614 continue;
2615 }
2616
2618 {
2619 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skip", guid, data.id);
2620 continue;
2621 }
2622
2623 if (!data.rotation.isUnit())
2624 {
2625 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);
2627 }
2628
2629 if (data.phaseMask == 0)
2630 {
2631 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
2632 data.phaseMask = 1;
2633 }
2634
2636 {
2637 uint32 zoneId = 0;
2638 uint32 areaId = 0;
2639 sMapMgr->GetZoneAndAreaId(data.phaseMask, zoneId, areaId, data.mapId, data.spawnPoint);
2640
2642
2643 stmt->setUInt32(0, zoneId);
2644 stmt->setUInt32(1, areaId);
2645 stmt->setUInt64(2, guid);
2646
2647 WorldDatabase.Execute(stmt);
2648 }
2649
2650 if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system
2651 AddGameobjectToGrid(guid, &data);
2652 }
2653 while (result->NextRow());
2654
2655 TC_LOG_INFO("server.loading", ">> Loaded {} gameobjects in {} ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2656}
2657
2659{
2660 uint32 oldMSTime = getMSTime();
2661
2662 // 0 1 2
2663 QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
2664
2665 if (result)
2666 {
2667 do
2668 {
2669 Field* fields = result->Fetch();
2670 uint32 groupId = fields[0].GetUInt32();
2672 group.groupId = groupId;
2673 group.name = fields[1].GetString();
2675 uint32 flags = fields[2].GetUInt32();
2677 {
2679 TC_LOG_ERROR("sql.sql", "Invalid spawn group flag {} on group ID {} ({}), reduced to valid flag {}.", flags, groupId, group.name, uint32(group.flags));
2680 }
2682 {
2683 flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
2684 TC_LOG_ERROR("sql.sql", "System spawn group {} ({}) has invalid manual spawn flag. Ignored.", groupId, group.name);
2685 }
2686 group.flags = SpawnGroupFlags(flags);
2687 } while (result->NextRow());
2688 }
2689
2690 if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
2691 {
2692 TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
2694 data.groupId = 0;
2695 data.name = "Default Group";
2696 data.mapId = 0;
2698 }
2699 if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
2700 {
2701 TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
2703 data.groupId = 1;
2704 data.name = "Legacy Group";
2705 data.mapId = 0;
2707 }
2708
2709 if (result)
2710 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group templates in {} ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2711 else
2712 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
2713
2714 return;
2715}
2716
2718{
2719 uint32 oldMSTime = getMSTime();
2720
2721 // 0 1 2
2722 QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
2723
2724 if (!result)
2725 {
2726 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
2727 return;
2728 }
2729
2730 uint32 numMembers = 0;
2731 do
2732 {
2733 Field* fields = result->Fetch();
2734 uint32 groupId = fields[0].GetUInt32();
2735 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
2736 if (!SpawnData::TypeIsValid(spawnType))
2737 {
2738 TC_LOG_ERROR("sql.sql", "Spawn data with invalid type {} listed for spawn group {}. Skipped.", uint32(spawnType), groupId);
2739 continue;
2740 }
2741 ObjectGuid::LowType spawnId = fields[2].GetUInt32();
2742
2743 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
2744 if (!data)
2745 {
2746 TC_LOG_ERROR("sql.sql", "Spawn data with ID ({},{}) not found, but is listed as a member of spawn group {}!", uint32(spawnType), spawnId, groupId);
2747 continue;
2748 }
2749 else if (data->spawnGroupData->groupId)
2750 {
2751 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);
2752 continue;
2753 }
2754 auto it = _spawnGroupDataStore.find(groupId);
2755 if (it == _spawnGroupDataStore.end())
2756 {
2757 TC_LOG_ERROR("sql.sql", "Spawn group {} assigned to spawn ID ({},{}), but group does not exist!", groupId, uint32(spawnType), spawnId);
2758 continue;
2759 }
2760 else
2761 {
2762 SpawnGroupTemplateData& groupTemplate = it->second;
2763 if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
2764 groupTemplate.mapId = data->mapId;
2765 else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2766 {
2767 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);
2768 continue;
2769 }
2770 const_cast<SpawnMetadata*>(data)->spawnGroupData = &groupTemplate;
2771 if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2772 _spawnGroupMapStore.emplace(groupId, data);
2773 ++numMembers;
2774 }
2775 } while (result->NextRow());
2776
2777 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group members in {} ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
2778}
2779
2781{
2782 uint32 oldMSTime = getMSTime();
2783
2784 // 0 1 2 3 4
2785 QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
2786
2787 if (!result)
2788 {
2789 TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
2790 return;
2791 }
2792
2793 uint32 n = 0;
2794 do
2795 {
2796 Field* fields = result->Fetch();
2797 uint32 const spawnGroupId = fields[3].GetUInt32();
2798 auto it = _spawnGroupDataStore.find(spawnGroupId);
2799 if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
2800 {
2801 TC_LOG_ERROR("sql.sql", "Invalid spawn group {} specified for instance {}. Skipped.", spawnGroupId, fields[0].GetUInt16());
2802 continue;
2803 }
2804
2805 uint16 const instanceMapId = fields[0].GetUInt16();
2806 if (it->second.mapId != instanceMapId)
2807 {
2808 TC_LOG_ERROR("sql.sql", "Instance spawn group {} specified for instance {} has spawns on a different map {}. Skipped.",
2809 spawnGroupId, instanceMapId, it->second.mapId);
2810 continue;
2811 }
2812
2813 InstanceSpawnGroupInfo& info = _instanceSpawnGroupStore[instanceMapId].emplace_back();
2814 info.SpawnGroupId = spawnGroupId;
2815 info.BossStateId = fields[1].GetUInt8();
2816
2817 uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
2818 uint8 const states = fields[2].GetUInt8();
2819 if (states & ~ALL_STATES)
2820 {
2821 info.BossStates = states & ALL_STATES;
2822 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid boss state mask {} - truncated to {}.", instanceMapId, spawnGroupId, states, info.BossStates);
2823 }
2824 else
2825 info.BossStates = states;
2826
2827 uint8 const flags = fields[4].GetUInt8();
2829 {
2831 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid flags {} - truncated to {}.", instanceMapId, spawnGroupId, flags, info.Flags);
2832 }
2833 else
2834 info.Flags = flags;
2835
2837 {
2839 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);
2840 }
2841
2842 ++n;
2843 } while (result->NextRow());
2844
2845 TC_LOG_INFO("server.loading", ">> Loaded {} instance spawn groups in {} ms", n, GetMSTimeDiffToNow(oldMSTime));
2846}
2847
2849{
2850 auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
2851 ASSERT(templateIt != _spawnGroupDataStore.end(), "Creature data for (%u,%u) is being deleted and has invalid spawn group index %u!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
2852 if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
2853 return;
2854
2855 auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
2856 for (auto it = pair.first; it != pair.second; ++it)
2857 {
2858 if (it->second != data)
2859 continue;
2860 _spawnGroupMapStore.erase(it);
2861 return;
2862 }
2863 ABORT_MSG("Spawn data (%u,%u) being removed is member of spawn group %u, but not actually listed in the lookup table for that group!", uint32(data->type), data->spawnId, data->spawnGroupData->groupId);
2864}
2865
2867{
2868 uint8 mask = data->spawnMask;
2869 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2870 {
2871 if (mask & 1)
2872 {
2874 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2875 cell_guids.gameobjects.insert(guid);
2876 }
2877 }
2878}
2879
2881{
2882 uint8 mask = data->spawnMask;
2883 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2884 {
2885 if (mask & 1)
2886 {
2888 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2889 cell_guids.gameobjects.erase(guid);
2890 }
2891 }
2892}
2893
2895{
2896 uint32 oldMSTime = getMSTime();
2897
2898 _itemLocaleStore.clear(); // need for reload case
2899
2900 // 0 1 2 3
2901 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name, Description FROM item_template_locale");
2902 if (!result)
2903 return;
2904
2905 do
2906 {
2907 Field* fields = result->Fetch();
2908
2909 uint32 id = fields[0].GetUInt32();
2910 std::string localeName = fields[1].GetString();
2911
2912 LocaleConstant locale = GetLocaleByName(localeName);
2913 if (locale == LOCALE_enUS)
2914 continue;
2915
2916 ItemLocale& data = _itemLocaleStore[id];
2917 AddLocaleString(fields[2].GetString(), locale, data.Name);
2918 AddLocaleString(fields[3].GetString(), locale, data.Description);
2919 } while (result->NextRow());
2920
2921 TC_LOG_INFO("server.loading", ">> Loaded {} Item locale strings in {} ms", uint32(_itemLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
2922}
2923
2925{
2926 uint32 oldMSTime = getMSTime();
2927
2928 // 0 1 2 3 4 5 6 7 8 9 10 11 12
2929 QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, "
2930 // 13 14 15 16 17 18 19 20
2931 "AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, "
2932 // 21 22 23 24 25 26 27 28
2933 "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, "
2934 // 29 30 31 32 33 34 35 36 37 38
2935 "stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, "
2936 // 39 40 41 42 43 44 45 46 47
2937 "stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, "
2938 // 48 49 50 51 52 53 54 55 56 57 58
2939 "ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, "
2940 // 59 60 61 62 63 64 65 66 67 68
2941 "nature_res, frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, "
2942 // 69 70 71 72 73 74 75
2943 "spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, "
2944 // 76 77 78 79 80 81 82
2945 "spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, "
2946 // 83 84 85 86 87 88 89
2947 "spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, "
2948 // 90 91 92 93 94 95 96
2949 "spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, "
2950 // 97 98 99 100 101 102 103 104 105
2951 "spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, "
2952 // 106 107 108 109 110 111 112 113 114 115 116 117
2953 "startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, "
2954 // 118 119 120 121 122 123 124 125
2955 "TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, "
2956 // 126 127 128 129 130 131 132 133
2957 "GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, "
2958 // 134 135 136
2959 "FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template");
2960
2961 if (!result)
2962 {
2963 TC_LOG_INFO("server.loading", ">> Loaded 0 item templates. DB table `item_template` is empty.");
2964 return;
2965 }
2966
2967 _itemTemplateStore.reserve(result->GetRowCount());
2968 bool enforceDBCAttributes = sWorld->getBoolConfig(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES);
2969
2970 do
2971 {
2972 Field* fields = result->Fetch();
2973
2974 uint32 entry = fields[0].GetUInt32();
2975 ItemTemplate& itemTemplate = _itemTemplateStore[entry];
2976
2977 itemTemplate.ItemId = entry;
2978 itemTemplate.Class = uint32(fields[1].GetUInt8());
2979 itemTemplate.SubClass = uint32(fields[2].GetUInt8());
2980 itemTemplate.SoundOverrideSubclass = int32(fields[3].GetInt8());
2981 itemTemplate.Name1 = fields[4].GetString();
2982 itemTemplate.DisplayInfoID = fields[5].GetUInt32();
2983 itemTemplate.Quality = uint32(fields[6].GetUInt8());
2984 itemTemplate.Flags[0] = fields[7].GetUInt32();
2985 itemTemplate.Flags[1] = fields[8].GetUInt32();
2986 itemTemplate.BuyCount = uint32(fields[9].GetUInt8());
2987 itemTemplate.BuyPrice = int32(fields[10].GetInt64());
2988 itemTemplate.SellPrice = fields[11].GetUInt32();
2989 itemTemplate.InventoryType = uint32(fields[12].GetUInt8());
2990 itemTemplate.AllowableClass = fields[13].GetInt32();
2991 itemTemplate.AllowableRace = fields[14].GetInt32();
2992 itemTemplate.ItemLevel = uint32(fields[15].GetUInt16());
2993 itemTemplate.RequiredLevel = uint32(fields[16].GetUInt8());
2994 itemTemplate.RequiredSkill = uint32(fields[17].GetUInt16());
2995 itemTemplate.RequiredSkillRank = uint32(fields[18].GetUInt16());
2996 itemTemplate.RequiredSpell = fields[19].GetUInt32();
2997 itemTemplate.RequiredHonorRank = fields[20].GetUInt32();
2998 itemTemplate.RequiredCityRank = fields[21].GetUInt32();
2999 itemTemplate.RequiredReputationFaction = uint32(fields[22].GetUInt16());
3000 itemTemplate.RequiredReputationRank = uint32(fields[23].GetUInt16());
3001 itemTemplate.MaxCount = fields[24].GetInt32();
3002 itemTemplate.Stackable = fields[25].GetInt32();
3003 itemTemplate.ContainerSlots = uint32(fields[26].GetUInt8());
3004 itemTemplate.StatsCount = uint32(fields[27].GetUInt8());
3005
3006 if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
3007 {
3008 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in statscount ({}), replace by hardcoded limit ({}).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
3009 itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
3010 }
3011
3012 for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
3013 {
3014 itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i*2].GetUInt8());
3015 itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i*2].GetInt16());
3016 }
3017
3018 itemTemplate.ScalingStatDistribution = uint32(fields[48].GetUInt16());
3019 itemTemplate.ScalingStatValue = fields[49].GetInt32();
3020
3021 for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
3022 {
3023 itemTemplate.Damage[i].DamageMin = fields[50 + i*3].GetFloat();
3024 itemTemplate.Damage[i].DamageMax = fields[51 + i*3].GetFloat();
3025 itemTemplate.Damage[i].DamageType = uint32(fields[52 + i*3].GetUInt8());
3026 }
3027
3028 itemTemplate.Armor = uint32(fields[56].GetUInt16());
3029 itemTemplate.HolyRes = uint32(fields[57].GetUInt8());
3030 itemTemplate.FireRes = uint32(fields[58].GetUInt8());
3031 itemTemplate.NatureRes = uint32(fields[59].GetUInt8());
3032 itemTemplate.FrostRes = uint32(fields[60].GetUInt8());
3033 itemTemplate.ShadowRes = uint32(fields[61].GetUInt8());
3034 itemTemplate.ArcaneRes = uint32(fields[62].GetUInt8());
3035 itemTemplate.Delay = uint32(fields[63].GetUInt16());
3036 itemTemplate.AmmoType = uint32(fields[64].GetUInt8());
3037 itemTemplate.RangedModRange = fields[65].GetFloat();
3038
3039 for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
3040 {
3041 itemTemplate.Effects[i].SpellID = fields[66 + i * 7].GetInt32();
3042 itemTemplate.Effects[i].TriggerType = uint32(fields[67 + i * 7].GetUInt8());
3043 itemTemplate.Effects[i].Charges = int32(fields[68 + i * 7].GetInt16());
3044 itemTemplate.Effects[i].SpellPPMRate = fields[69 + i * 7].GetFloat();
3045 itemTemplate.Effects[i].CoolDownMSec = fields[70 + i * 7].GetInt32();
3046 itemTemplate.Effects[i].SpellCategoryID = uint32(fields[71 + i * 7].GetUInt16());
3047 itemTemplate.Effects[i].CategoryCoolDownMSec = fields[72 + i * 7].GetInt32();
3048 }
3049
3050 itemTemplate.Bonding = uint32(fields[101].GetUInt8());
3051 itemTemplate.Description = fields[102].GetString();
3052 itemTemplate.PageText = fields[103].GetUInt32();
3053 itemTemplate.LanguageID = uint32(fields[104].GetUInt8());
3054 itemTemplate.PageMaterial = uint32(fields[105].GetUInt8());
3055 itemTemplate.StartQuest = fields[106].GetUInt32();
3056 itemTemplate.LockID = fields[107].GetUInt32();
3057 itemTemplate.Material = int32(fields[108].GetInt8());
3058 itemTemplate.Sheath = uint32(fields[109].GetUInt8());
3059 itemTemplate.RandomProperty = fields[110].GetUInt32();
3060 itemTemplate.RandomSuffix = fields[111].GetInt32();
3061 itemTemplate.Block = fields[112].GetUInt32();
3062 itemTemplate.ItemSet = fields[113].GetUInt32();
3063 itemTemplate.MaxDurability = uint32(fields[114].GetUInt16());
3064 itemTemplate.Area = fields[115].GetUInt32();
3065 itemTemplate.Map = uint32(fields[116].GetUInt16());
3066 itemTemplate.BagFamily = fields[117].GetUInt32();
3067 itemTemplate.TotemCategory = fields[118].GetUInt32();
3068
3069 for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
3070 {
3071 itemTemplate.Socket[i].Color = uint32(fields[119 + i*2].GetUInt8());
3072 itemTemplate.Socket[i].Content = fields[120 + i*2].GetUInt32();
3073 }
3074
3075 itemTemplate.socketBonus = fields[125].GetUInt32();
3076 itemTemplate.GemProperties = fields[126].GetUInt32();
3077 itemTemplate.RequiredDisenchantSkill = uint32(fields[127].GetInt16());
3078 itemTemplate.ArmorDamageModifier = fields[128].GetFloat();
3079 itemTemplate.Duration = fields[129].GetUInt32();
3080 itemTemplate.ItemLimitCategory = uint32(fields[130].GetInt16());
3081 itemTemplate.HolidayId = fields[131].GetUInt32();
3082 itemTemplate.ScriptId = sObjectMgr->GetScriptId(fields[132].GetString());
3083 itemTemplate.DisenchantID = fields[133].GetUInt32();
3084 itemTemplate.FoodType = uint32(fields[134].GetUInt8());
3085 itemTemplate.MinMoneyLoot = fields[135].GetUInt32();
3086 itemTemplate.MaxMoneyLoot = fields[136].GetUInt32();
3087 itemTemplate.FlagsCu = fields[137].GetUInt32();
3088
3089 // Checks
3090
3091 ItemEntry const* dbcitem = sItemStore.LookupEntry(entry);
3092
3093 if (dbcitem)
3094 {
3095 if (itemTemplate.Class != dbcitem->ClassID)
3096 {
3097 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct class {}, must be {} .", entry, itemTemplate.Class, dbcitem->ClassID);
3098 if (enforceDBCAttributes)
3099 itemTemplate.Class = dbcitem->ClassID;
3100 }
3101
3102 if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclassID)
3103 {
3104 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct SoundOverrideSubclass ({}), must be {} .", entry, itemTemplate.SoundOverrideSubclass, dbcitem->SoundOverrideSubclassID);
3105 if (enforceDBCAttributes)
3106 itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclassID;
3107 }
3108 if (itemTemplate.Material != dbcitem->Material)
3109 {
3110 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct material ({}), must be {} .", entry, itemTemplate.Material, dbcitem->Material);
3111 if (enforceDBCAttributes)
3112 itemTemplate.Material = dbcitem->Material;
3113 }
3114 if (itemTemplate.InventoryType != dbcitem->InventoryType)
3115 {
3116 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct inventory type ({}), must be {} .", entry, itemTemplate.InventoryType, dbcitem->InventoryType);
3117 if (enforceDBCAttributes)
3118 itemTemplate.InventoryType = dbcitem->InventoryType;
3119 }
3120 if (itemTemplate.DisplayInfoID != dbcitem->DisplayInfoID)
3121 {
3122 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct display id ({}), must be {} .", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayInfoID);
3123 if (enforceDBCAttributes)
3124 itemTemplate.DisplayInfoID = dbcitem->DisplayInfoID;
3125 }
3126 if (itemTemplate.Sheath != dbcitem->SheatheType)
3127 {
3128 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct sheathid ({}), must be {} .", entry, itemTemplate.Sheath, dbcitem->SheatheType);
3129 if (enforceDBCAttributes)
3130 itemTemplate.Sheath = dbcitem->SheatheType;
3131 }
3132
3133 }
3134 else
3135 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not exist in item.dbc! (not correct id?).", entry);
3136
3137 if (itemTemplate.Class >= MAX_ITEM_CLASS)
3138 {
3139 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Class value ({})", entry, itemTemplate.Class);
3140 itemTemplate.Class = ITEM_CLASS_MISCELLANEOUS;
3141 }
3142
3143 if (itemTemplate.SubClass >= MaxItemSubclassValues[itemTemplate.Class])
3144 {
3145 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Subclass value ({}) for class {}", entry, itemTemplate.SubClass, itemTemplate.Class);
3146 itemTemplate.SubClass = 0;// exist for all item classes
3147 }
3148
3149 if (itemTemplate.Quality >= MAX_ITEM_QUALITY)
3150 {
3151 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Quality value ({})", entry, itemTemplate.Quality);
3152 itemTemplate.Quality = ITEM_QUALITY_NORMAL;
3153 }
3154
3155 if (itemTemplate.HasFlag(ITEM_FLAG2_FACTION_HORDE))
3156 {
3157 if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE))
3158 if ((itemTemplate.AllowableRace & faction->ReputationRaceMask[0]) == 0)
3159 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has value ({}) in `AllowableRace` races, not compatible with ITEM_FLAG2_FACTION_HORDE ({}) in Flags field, item cannot be equipped or used by these races.",
3160 entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_HORDE);
3161
3162 if (itemTemplate.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
3163 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has value ({}) in `Flags2` flags (ITEM_FLAG2_FACTION_ALLIANCE) and ITEM_FLAG2_FACTION_HORDE ({}) in Flags field, this is a wrong combination.",
3165 }
3166 else if (itemTemplate.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
3167 {
3168 if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE))
3169 if ((itemTemplate.AllowableRace & faction->ReputationRaceMask[0]) == 0)
3170 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has value ({}) in `AllowableRace` races, not compatible with ITEM_FLAG2_FACTION_ALLIANCE ({}) in Flags field, item cannot be equipped or used by these races.",
3171 entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_ALLIANCE);
3172 }
3173
3174 if (itemTemplate.BuyCount <= 0)
3175 {
3176 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong BuyCount value ({}), set to default(1).", entry, itemTemplate.BuyCount);
3177 itemTemplate.BuyCount = 1;
3178 }
3179
3180 if (itemTemplate.InventoryType >= MAX_INVTYPE)
3181 {
3182 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong InventoryType value ({})", entry, itemTemplate.InventoryType);
3183 itemTemplate.InventoryType = INVTYPE_NON_EQUIP;
3184 }
3185
3186 if (itemTemplate.RequiredSkill >= MAX_SKILL_TYPE)
3187 {
3188 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong RequiredSkill value ({})", entry, itemTemplate.RequiredSkill);
3189 itemTemplate.RequiredSkill = 0;
3190 }
3191
3192 {
3193 // can be used in equip slot, as page read use in inventory, or spell casting at use
3194 bool req = itemTemplate.InventoryType != INVTYPE_NON_EQUIP || itemTemplate.PageText;
3195 if (!req)
3196 for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
3197 {
3198 if (itemTemplate.Effects[j].SpellID > 0)
3199 {
3200 req = true;
3201 break;
3202 }
3203 }
3204
3205 if (req)
3206 {
3207 if (!(itemTemplate.AllowableClass & CLASSMASK_ALL_PLAYABLE))
3208 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have any playable classes ({}) in `AllowableClass` and can't be equipped or used.", entry, itemTemplate.AllowableClass);
3209
3210 if (!(itemTemplate.AllowableRace & RACEMASK_ALL_PLAYABLE))
3211 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have any playable races ({}) in `AllowableRace` and can't be equipped or used.", entry, itemTemplate.AllowableRace);
3212 }
3213 }
3214
3215 if (itemTemplate.RequiredSpell && !sSpellMgr->GetSpellInfo(itemTemplate.RequiredSpell))
3216 {
3217 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has a wrong (non-existing) spell in RequiredSpell ({})", entry, itemTemplate.RequiredSpell);
3218 itemTemplate.RequiredSpell = 0;
3219 }
3220
3221 if (itemTemplate.RequiredReputationRank >= MAX_REPUTATION_RANK)
3222 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong reputation rank in RequiredReputationRank ({}), item can't be used.", entry, itemTemplate.RequiredReputationRank);
3223
3224 if (itemTemplate.RequiredReputationFaction)
3225 {
3226 if (!sFactionStore.LookupEntry(itemTemplate.RequiredReputationFaction))
3227 {
3228 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (not existing) faction in RequiredReputationFaction ({})", entry, itemTemplate.RequiredReputationFaction);
3229 itemTemplate.RequiredReputationFaction = 0;
3230 }
3231
3232 if (itemTemplate.RequiredReputationRank == MIN_REPUTATION_RANK)
3233 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", entry);
3234 }
3235
3236 if (itemTemplate.MaxCount < -1)
3237 {
3238 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large negative in maxcount ({}), replace by value (-1) no storing limits.", entry, itemTemplate.MaxCount);
3239 itemTemplate.MaxCount = -1;
3240 }
3241
3242 if (itemTemplate.Stackable == 0)
3243 {
3244 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong value in stackable ({}), replace by default 1.", entry, itemTemplate.Stackable);
3245 itemTemplate.Stackable = 1;
3246 }
3247 else if (itemTemplate.Stackable < -1)
3248 {
3249 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large negative in stackable ({}), replace by value (-1) no stacking limits.", entry, itemTemplate.Stackable);
3250 itemTemplate.Stackable = -1;
3251 }
3252
3253 if (itemTemplate.ContainerSlots > MAX_BAG_SIZE)
3254 {
3255 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in ContainerSlots ({}), replace by hardcoded limit ({}).", entry, itemTemplate.ContainerSlots, MAX_BAG_SIZE);
3256 itemTemplate.ContainerSlots = MAX_BAG_SIZE;
3257 }
3258
3259 for (uint8 j = 0; j < itemTemplate.StatsCount; ++j)
3260 {
3261 // for ItemStatValue != 0
3262 if (itemTemplate.ItemStat[j].ItemStatValue && itemTemplate.ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
3263 {
3264 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (non-existing?) stat_type{} ({})", entry, j+1, itemTemplate.ItemStat[j].ItemStatType);
3265 itemTemplate.ItemStat[j].ItemStatType = 0;
3266 }
3267
3268 switch (itemTemplate.ItemStat[j].ItemStatType)
3269 {
3272 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j+1, itemTemplate.ItemStat[j].ItemStatType);
3273 break;
3274 default:
3275 break;
3276 }
3277 }
3278
3279 for (uint8 j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j)
3280 {
3281 if (itemTemplate.Damage[j].DamageType >= MAX_SPELL_SCHOOL)
3282 {
3283 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong dmg_type{} ({})", entry, j+1, itemTemplate.Damage[j].DamageType);
3284 itemTemplate.Damage[j].DamageType = 0;
3285 }
3286 }
3287
3288 for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
3289 {
3290 if (itemTemplate.Effects[j].TriggerType >= MAX_ITEM_SPELLTRIGGER)
3291 {
3292 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong item spell trigger value in spelltrigger_{} ({})", entry, j+1, itemTemplate.Effects[j].TriggerType);
3293 itemTemplate.Effects[j].SpellID = 0;
3294 itemTemplate.Effects[j].TriggerType = ITEM_SPELLTRIGGER_ON_USE;
3295 }
3296
3297 if (itemTemplate.Effects[j].SpellID > 0)
3298 {
3299 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Effects[j].SpellID);
3300 if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Effects[j].SpellID, nullptr))
3301 {
3302 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (not existing) spell in spellid_{} ({})", entry, j+1, itemTemplate.Effects[j].SpellID);
3303 itemTemplate.Effects[j].SpellID = 0;
3304 }
3305 }
3306 }
3307
3308 if (itemTemplate.Bonding >= MAX_BIND_TYPE)
3309 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Bonding value ({})", entry, itemTemplate.Bonding);
3310
3311 if (itemTemplate.PageText && !GetPageText(itemTemplate.PageText))
3312 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has non existing first page (Id:{})", entry, itemTemplate.PageText);
3313
3314 if (itemTemplate.LockID && !sLockStore.LookupEntry(itemTemplate.LockID))
3315 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong LockID ({})", entry, itemTemplate.LockID);
3316
3317 if (itemTemplate.Sheath >= MAX_SHEATHETYPE)
3318 {
3319 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Sheath ({})", entry, itemTemplate.Sheath);
3320 itemTemplate.Sheath = SHEATHETYPE_NONE;
3321 }
3322
3323 if (itemTemplate.RandomProperty)
3324 {
3325 // To be implemented later
3326 if (itemTemplate.RandomProperty == -1)
3327 itemTemplate.RandomProperty = 0;
3328
3329 else if (!sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomProperty)))
3330 {
3331 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty ({})", entry, itemTemplate.RandomProperty);
3332 itemTemplate.RandomProperty = 0;
3333 }
3334 }
3335
3336 if (itemTemplate.RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomSuffix)))
3337 {
3338 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong RandomSuffix ({})", entry, itemTemplate.RandomSuffix);
3339 itemTemplate.RandomSuffix = 0;
3340 }
3341
3342 if (itemTemplate.ItemSet && !sItemSetStore.LookupEntry(itemTemplate.ItemSet))
3343 {
3344 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) have wrong ItemSet ({})", entry, itemTemplate.ItemSet);
3345 itemTemplate.ItemSet = 0;
3346 }
3347
3348 if (itemTemplate.Area && !sAreaTableStore.LookupEntry(itemTemplate.Area))
3349 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Area ({})", entry, itemTemplate.Area);
3350
3351 if (itemTemplate.Map && !sMapStore.LookupEntry(itemTemplate.Map))
3352 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Map ({})", entry, itemTemplate.Map);
3353
3354 if (itemTemplate.BagFamily)
3355 {
3356 // check bits
3357 for (uint32 j = 0; j < sizeof(itemTemplate.BagFamily)*8; ++j)
3358 {
3359 uint32 mask = 1 << j;
3360 if ((itemTemplate.BagFamily & mask) == 0)
3361 continue;
3362
3363 ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1);
3364 if (!bf)
3365 {
3366 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", entry);
3367 itemTemplate.BagFamily &= ~mask;
3368 continue;
3369 }
3370
3372 {
3373 CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemTemplate.ItemId);
3374 if (!ctEntry)
3375 {
3376 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit", entry);
3377 itemTemplate.BagFamily &= ~mask;
3378 }
3379 }
3380 }
3381 }
3382
3383 if (itemTemplate.TotemCategory && !sTotemCategoryStore.LookupEntry(itemTemplate.TotemCategory))
3384 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong TotemCategory ({})", entry, itemTemplate.TotemCategory);
3385
3386 for (uint8 j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
3387 {
3388 if (itemTemplate.Socket[j].Color && (itemTemplate.Socket[j].Color & SOCKET_COLOR_ALL) != itemTemplate.Socket[j].Color)
3389 {
3390 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong socketColor_{} ({})", entry, j+1, itemTemplate.Socket[j].Color);
3391 itemTemplate.Socket[j].Color = 0;
3392 }
3393 }
3394
3395 if (itemTemplate.GemProperties && !sGemPropertiesStore.LookupEntry(itemTemplate.GemProperties))
3396 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong GemProperties ({})", entry, itemTemplate.GemProperties);
3397
3398 if (itemTemplate.FoodType >= MAX_PET_DIET)
3399 {
3400 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong FoodType value ({})", entry, itemTemplate.FoodType);
3401 itemTemplate.FoodType = 0;
3402 }
3403
3404 if (itemTemplate.ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(itemTemplate.ItemLimitCategory))
3405 {
3406 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong LimitCategory value ({})", entry, itemTemplate.ItemLimitCategory);
3407 itemTemplate.ItemLimitCategory = 0;
3408 }
3409
3410 if (itemTemplate.HolidayId && !sHolidaysStore.LookupEntry(itemTemplate.HolidayId))
3411 {
3412 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong HolidayId value ({})", entry, itemTemplate.HolidayId);
3413 itemTemplate.HolidayId = 0;
3414 }
3415
3416 if (itemTemplate.FlagsCu & ITEM_FLAGS_CU_DURATION_REAL_TIME && !itemTemplate.Duration)
3417 {
3418 TC_LOG_ERROR("sql.sql", "Item (Entry {}) has flag ITEM_FLAGS_CU_DURATION_REAL_TIME but it does not have duration limit", entry);
3419 itemTemplate.FlagsCu &= ~ITEM_FLAGS_CU_DURATION_REAL_TIME;
3420 }
3421
3422 // Load cached data
3423 itemTemplate._LoadTotalAP();
3424 } while (result->NextRow());
3425
3426 // Check if item templates for DBC referenced character start outfit are present
3427 std::set<uint32> notFoundOutfit;
3428 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
3429 {
3430 CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
3431 if (!entry)
3432 continue;
3433
3434 for (uint8 j = 0; j < MAX_OUTFIT_ITEMS; ++j)
3435 {
3436 if (entry->ItemID[j] <= 0)
3437 continue;
3438
3439 uint32 item_id = entry->ItemID[j];
3440
3441 if (!GetItemTemplate(item_id))
3442 notFoundOutfit.insert(item_id);
3443 }
3444 }
3445
3446 for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
3447 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not exist in `item_template` but is referenced in `CharStartOutfit.dbc`", *itr);
3448
3449 TC_LOG_INFO("server.loading", ">> Loaded {} item templates in {} ms", _itemTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3450}
3451
3456
3458{
3459 uint32 oldMSTime = getMSTime();
3460
3461 _itemSetNameLocaleStore.clear(); // need for reload case
3462
3463 // 0 1 2
3464 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM item_set_names_locale");
3465 if (!result)
3466 return;
3467
3468 do
3469 {
3470 Field* fields = result->Fetch();
3471
3472 uint32 id = fields[0].GetUInt32();
3473 std::string localeName = fields[1].GetString();
3474
3475 LocaleConstant locale = GetLocaleByName(localeName);
3476 if (locale == LOCALE_enUS)
3477 continue;
3478
3480 AddLocaleString(fields[2].GetString(), locale, data.Name);
3481 } while (result->NextRow());
3482
3483 TC_LOG_INFO("server.loading", ">> Loaded {} Item set name locale strings in {} ms", uint64(_itemSetNameLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
3484}
3485
3487{
3488 uint32 oldMSTime = getMSTime();
3489
3490 _itemSetNameStore.clear(); // needed for reload case
3491
3492 std::set<uint32> itemSetItems;
3493
3494 // fill item set member ids
3495 for (uint32 entryId = 0; entryId < sItemSetStore.GetNumRows(); ++entryId)
3496 {
3497 ItemSetEntry const* setEntry = sItemSetStore.LookupEntry(entryId);
3498 if (!setEntry)
3499 continue;
3500
3501 for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i)
3502 if (setEntry->ItemID[i])
3503 itemSetItems.insert(setEntry->ItemID[i]);
3504 }
3505
3506 // 0 1 2
3507 QueryResult result = WorldDatabase.Query("SELECT `entry`, `name`, `InventoryType` FROM `item_set_names`");
3508
3509 if (!result)
3510 {
3511 TC_LOG_INFO("server.loading", ">> Loaded 0 item set names. DB table `item_set_names` is empty.");
3512 return;
3513 }
3514
3515 _itemSetNameStore.rehash(result->GetRowCount());
3516 uint32 count = 0;
3517
3518 do
3519 {
3520 Field* fields = result->Fetch();
3521
3522 uint32 entry = fields[0].GetUInt32();
3523 if (itemSetItems.find(entry) == itemSetItems.end())
3524 {
3525 TC_LOG_ERROR("sql.sql", "Item set name (Entry: {}) not found in ItemSet.dbc, data useless.", entry);
3526 continue;
3527 }
3528
3529 ItemSetNameEntry &data = _itemSetNameStore[entry];
3530 data.name = fields[1].GetString();
3531
3532 uint32 invType = fields[2].GetUInt8();
3533 if (invType >= MAX_INVTYPE)
3534 {
3535 TC_LOG_ERROR("sql.sql", "Item set name (Entry: {}) has wrong InventoryType value ({})", entry, invType);
3536 invType = INVTYPE_NON_EQUIP;
3537 }
3538
3539 data.InventoryType = invType;
3540 itemSetItems.erase(entry);
3541 ++count;
3542 } while (result->NextRow());
3543
3544 if (!itemSetItems.empty())
3545 {
3546 ItemTemplate const* pProto;
3547 for (std::set<uint32>::iterator itr = itemSetItems.begin(); itr != itemSetItems.end(); ++itr)
3548 {
3549 uint32 entry = *itr;
3550 // add data from item_template if available
3551 pProto = sObjectMgr->GetItemTemplate(entry);
3552 if (pProto)
3553 {
3554 TC_LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, adding data from `item_template`.", entry);
3555 ItemSetNameEntry &data = _itemSetNameStore[entry];
3556 data.name = pProto->Name1;
3557 data.InventoryType = pProto->InventoryType;
3558 ++count;
3559 }
3560 else
3561 TC_LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, set will not display properly.", entry);
3562 }
3563 }
3564
3565 TC_LOG_INFO("server.loading", ">> Loaded {} item set names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3566}
3567
3569{
3570 uint32 oldMSTime = getMSTime();
3571
3572 _vehicleTemplateAccessoryStore.clear(); // needed for reload case
3573
3574 uint32 count = 0;
3575
3576 // 0 1 2 3 4 5
3577 QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
3578
3579 if (!result)
3580 {
3581 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
3582 return;
3583 }
3584
3585 do
3586 {
3587 Field* fields = result->Fetch();
3588
3589 uint32 entry = fields[0].GetUInt32();
3590 uint32 accessory = fields[1].GetUInt32();
3591 int8 seatId = fields[2].GetInt8();
3592 bool isMinion = fields[3].GetBool();
3593 uint8 summonType = fields[4].GetUInt8();
3594 uint32 summonTimer = fields[5].GetUInt32();
3595
3596 if (!sObjectMgr->GetCreatureTemplate(entry))
3597 {
3598 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", entry);
3599 continue;
3600 }
3601
3602 if (!sObjectMgr->GetCreatureTemplate(accessory))
3603 {
3604 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", accessory);
3605 continue;
3606 }
3607
3608 if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
3609 {
3610 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} has no data in npc_spellclick_spells", entry);
3611 continue;
3612 }
3613
3614 _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
3615
3616 ++count;
3617 }
3618 while (result->NextRow());
3619
3620 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3621}
3622
3624{
3625 uint32 oldMSTime = getMSTime();
3626
3627 _vehicleTemplateStore.clear();
3628
3629 // 0 1
3630 QueryResult result = WorldDatabase.Query("SELECT creatureId, despawnDelayMs FROM vehicle_template");
3631
3632 if (!result)
3633 {
3634 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template. DB table `vehicle_template` is empty.");
3635 return;
3636 }
3637
3638 do
3639 {
3640 Field* fields = result->Fetch();
3641
3642 uint32 creatureId = fields[0].GetUInt32();
3643
3644 if (!sObjectMgr->GetCreatureTemplate(creatureId))
3645 {
3646 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Vehicle {} does not exist.", creatureId);
3647 continue;
3648 }
3649
3650 VehicleTemplate& vehicleTemplate = _vehicleTemplateStore[creatureId];
3651 vehicleTemplate.DespawnDelay = Milliseconds(fields[1].GetInt32());
3652
3653 } while (result->NextRow());
3654
3655 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template entries in {} ms", _vehicleTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3656}
3657
3659{
3660 uint32 oldMSTime = getMSTime();
3661
3662 _vehicleAccessoryStore.clear(); // needed for reload case
3663
3664 uint32 count = 0;
3665
3666 // 0 1 2 3 4 5
3667 QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
3668
3669 if (!result)
3670 {
3671 TC_LOG_INFO("server.loading", ">> Loaded 0 Vehicle Accessories in {} ms", GetMSTimeDiffToNow(oldMSTime));
3672 return;
3673 }
3674
3675 do
3676 {
3677 Field* fields = result->Fetch();
3678
3679 ObjectGuid::LowType uiGUID = fields[0].GetUInt32();
3680 uint32 uiAccessory = fields[1].GetUInt32();
3681 int8 uiSeat = int8(fields[2].GetInt16());
3682 bool bMinion = fields[3].GetBool();
3683 uint8 uiSummonType = fields[4].GetUInt8();
3684 uint32 uiSummonTimer= fields[5].GetUInt32();
3685
3686 if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
3687 {
3688 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
3689 continue;
3690 }
3691
3692 _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
3693
3694 ++count;
3695 }
3696 while (result->NextRow());
3697
3698 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3699}
3700
3702{
3703 uint32 oldMSTime = getMSTime();
3704
3705 _vehicleSeatAddonStore.clear(); // needed for reload case
3706
3707 uint32 count = 0;
3708
3709 // 0 1 2 3 4 5 6
3710 QueryResult result = WorldDatabase.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
3711
3712 if (!result)
3713 {
3714 TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle seat addons. DB table `vehicle_seat_addon` is empty.");
3715 return;
3716 }
3717
3718 do
3719 {
3720 Field* fields = result->Fetch();
3721
3722 uint32 seatID = fields[0].GetUInt32();
3723 float orientation = fields[1].GetFloat();
3724 float exitX = fields[2].GetFloat();
3725 float exitY = fields[3].GetFloat();
3726 float exitZ = fields[4].GetFloat();
3727 float exitO = fields[5].GetFloat();
3728 uint8 exitParam = fields[6].GetUInt8();
3729
3730 if (!sVehicleSeatStore.LookupEntry(seatID))
3731 {
3732 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} does not exist in VehicleSeat.dbc. Skipping entry.", seatID);
3733 continue;
3734 }
3735
3736 // Sanitizing values
3737 if (orientation > float(M_PI * 2))
3738 {
3739 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid angle offset value ({}). Set Value to 0.", seatID, orientation);
3740 orientation = 0.0f;
3741 }
3742
3744 {
3745 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid exit parameter value ({}). Setting to 0 (none).", seatID, exitParam);
3746 continue;
3747 }
3748
3749 _vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
3750
3751 ++count;
3752 } while (result->NextRow());
3753
3754 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Seat Addon entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3755}
3756
3758{
3759 uint32 oldMSTime = getMSTime();
3760
3761 // 0 1 2 3 4 5 6 7 8 9 10 11
3762 QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor, min_dmg, max_dmg FROM pet_levelstats");
3763
3764 if (!result)
3765 {
3766 TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
3767 return;
3768 }
3769
3770 uint32 count = 0;
3771
3772 do
3773 {
3774 Field* fields = result->Fetch();
3775
3776 uint32 creature_id = fields[0].GetUInt32();
3777 if (!sObjectMgr->GetCreatureTemplate(creature_id))
3778 {
3779 TC_LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
3780 continue;
3781 }
3782
3783 uint32 current_level = fields[1].GetUInt8();
3784 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3785 {
3786 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3787 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3788 else
3789 {
3790 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `pet_levelstats` table, ignoring.", current_level);
3791 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3792 }
3793 continue;
3794 }
3795 else if (current_level < 1)
3796 {
3797 TC_LOG_ERROR("sql.sql", "Wrong (<1) level {} in `pet_levelstats` table, ignoring.", current_level);
3798 continue;
3799 }
3800
3801 auto& pInfoMapEntry = _petInfoStore[creature_id];
3802 if (!pInfoMapEntry)
3803 pInfoMapEntry = std::make_unique<PetLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
3804
3805 // data for level 1 stored in [0] array element, ...
3806 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
3807
3808 pLevelInfo->health = fields[2].GetUInt16();
3809 pLevelInfo->mana = fields[3].GetUInt16();
3810 pLevelInfo->armor = fields[9].GetUInt32();
3811 pLevelInfo->minDamage = fields[10].GetUInt16();
3812 pLevelInfo->maxDamage = fields[11].GetUInt16();
3813
3814 for (uint8 i = 0; i < MAX_STATS; i++)
3815 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
3816
3817 ++count;
3818 }
3819 while (result->NextRow());
3820
3821 // Fill gaps and check integrity
3822 for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
3823 {
3824 auto& pInfo = itr->second;
3825
3826 // fatal error if no level 1 data
3827 if (!pInfo || pInfo[0].health == 0)
3828 {
3829 TC_LOG_ERROR("sql.sql", "Creature {} does not have pet stats data for Level 1!", itr->first);
3830 ABORT();
3831 }
3832
3833 // fill level gaps
3834 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3835 {
3836 if (pInfo[level].health == 0)
3837 {
3838 TC_LOG_ERROR("sql.sql", "Creature {} has no data for Level {} pet stats data, using data of Level {}.", itr->first, level + 1, level);
3839 pInfo[level] = pInfo[level - 1];
3840 }
3841 }
3842 }
3843
3844 TC_LOG_INFO("server.loading", ">> Loaded {} level pet stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3845}
3846
3847PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
3848{
3849 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3850 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
3851
3852 auto itr = _petInfoStore.find(creature_id);
3853 if (itr == _petInfoStore.end())
3854 return nullptr;
3855
3856 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
3857}
3858
3860{
3861 if (!_playerInfo[race_][class_])
3862 return;
3863
3864 if (count > 0)
3865 _playerInfo[race_][class_]->item.push_back(PlayerCreateInfoItem(itemId, count));
3866 else
3867 {
3868 if (count < -1)
3869 TC_LOG_ERROR("sql.sql", "Invalid count {} specified on item {} be removed from original player create info (use -1)!", count, itemId);
3870
3871 for (uint32 gender = 0; gender < GENDER_NONE; ++gender)
3872 {
3873 if (CharStartOutfitEntry const* entry = GetCharStartOutfitEntry(race_, class_, gender))
3874 {
3875 bool found = false;
3876 for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
3877 {
3878 if (entry->ItemID[x] > 0 && uint32(entry->ItemID[x]) == itemId)
3879 {
3880 found = true;
3881 const_cast<CharStartOutfitEntry*>(entry)->ItemID[x] = 0;
3882 break;
3883 }
3884 }
3885
3886 if (!found)
3887 TC_LOG_ERROR("sql.sql", "Item {} specified to be removed from original create info not found in dbc!", itemId);
3888 }
3889 }
3890 }
3891}
3892
3894{
3895 // Load playercreate
3896 {
3897 uint32 oldMSTime = getMSTime();
3898 // 0 1 2 3 4 5 6
3899 QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
3900
3901 if (!result)
3902 {
3903 TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
3904 ABORT();
3905 }
3906 else
3907 {
3908 uint32 count = 0;
3909
3910 do
3911 {
3912 Field* fields = result->Fetch();
3913
3914 uint32 current_race = fields[0].GetUInt8();
3915 uint32 current_class = fields[1].GetUInt8();
3916 uint32 mapId = fields[2].GetUInt16();
3917 uint32 areaId = fields[3].GetUInt32(); // zone
3918 float positionX = fields[4].GetFloat();
3919 float positionY = fields[5].GetFloat();
3920 float positionZ = fields[6].GetFloat();
3921 float orientation = fields[7].GetFloat();
3922
3923 if (current_race >= MAX_RACES)
3924 {
3925 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3926 continue;
3927 }
3928
3929 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
3930 if (!rEntry)
3931 {
3932 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3933 continue;
3934 }
3935
3936 if (current_class >= MAX_CLASSES)
3937 {
3938 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3939 continue;
3940 }
3941
3942 if (!sChrClassesStore.LookupEntry(current_class))
3943 {
3944 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3945 continue;
3946 }
3947
3948 // accept DB data only for valid position (and non instanceable)
3949 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
3950 {
3951 TC_LOG_ERROR("sql.sql", "Wrong home position for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3952 continue;
3953 }
3954
3955 if (sMapStore.LookupEntry(mapId)->Instanceable())
3956 {
3957 TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3958 continue;
3959 }
3960
3961 std::unique_ptr<PlayerInfo> info = std::make_unique<PlayerInfo>();
3962 info->mapId = mapId;
3963 info->areaId = areaId;
3964 info->positionX = positionX;
3965 info->positionY = positionY;
3966 info->positionZ = positionZ;
3967 info->orientation = orientation;
3968 info->displayId_m = rEntry->MaleDisplayID;
3969 info->displayId_f = rEntry->FemaleDisplayID;
3970 _playerInfo[current_race][current_class] = std::move(info);
3971
3972 ++count;
3973 }
3974 while (result->NextRow());
3975
3976 TC_LOG_INFO("server.loading", ">> Loaded {} player create definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3977 }
3978 }
3979
3980 // Load playercreate items
3981 TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
3982 {
3983 uint32 oldMSTime = getMSTime();
3984 // 0 1 2 3
3985 QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
3986
3987 if (!result)
3988 {
3989 TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
3990 }
3991 else
3992 {
3993 uint32 count = 0;
3994
3995 do
3996 {
3997 Field* fields = result->Fetch();
3998
3999 uint32 current_race = fields[0].GetUInt8();
4000 if (current_race >= MAX_RACES)
4001 {
4002 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_item` table, ignoring.", current_race);
4003 continue;
4004 }
4005
4006 uint32 current_class = fields[1].GetUInt8();
4007 if (current_class >= MAX_CLASSES)
4008 {
4009 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_item` table, ignoring.", current_class);
4010 continue;
4011 }
4012
4013 uint32 item_id = fields[2].GetUInt32();
4014
4015 if (!GetItemTemplate(item_id))
4016 {
4017 TC_LOG_ERROR("sql.sql", "Item id {} (race {} class {}) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.", item_id, current_race, current_class);
4018 continue;
4019 }
4020
4021 int32 amount = fields[3].GetInt8();
4022
4023 if (!amount)
4024 {
4025 TC_LOG_ERROR("sql.sql", "Item id {} (class {} race {}) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
4026 continue;
4027 }
4028
4029 if (!current_race || !current_class)
4030 {
4031 uint32 min_race = current_race ? current_race : 1;
4032 uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
4033 uint32 min_class = current_class ? current_class : 1;
4034 uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
4035 for (uint32 r = min_race; r < max_race; ++r)
4036 for (uint32 c = min_class; c < max_class; ++c)
4037 PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
4038 }
4039 else
4040 PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
4041
4042 ++count;
4043 }
4044 while (result->NextRow());
4045
4046 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4047 }
4048 }
4049
4050 // Load playercreate skills
4051 TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
4052 {
4053 uint32 oldMSTime = getMSTime();
4054
4055 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, `rank` FROM playercreateinfo_skills");
4056
4057 if (!result)
4058 {
4059 TC_LOG_INFO("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
4060 }
4061 else
4062 {
4063 uint32 count = 0;
4064
4065 do
4066 {
4067 Field* fields = result->Fetch();
4068 uint32 raceMask = fields[0].GetUInt32();
4069 uint32 classMask = fields[1].GetUInt32();
4071 skill.SkillId = fields[2].GetUInt16();
4072 skill.Rank = fields[3].GetUInt16();
4073
4074 if (skill.Rank >= MAX_SKILL_STEP)
4075 {
4076 TC_LOG_ERROR("sql.sql", "Skill rank value {} set for skill {} raceMask {} classMask {} is too high, max allowed value is {}", skill.Rank, skill.SkillId, raceMask, classMask, MAX_SKILL_STEP);
4077 continue;
4078 }
4079
4080 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
4081 {
4082 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_skills` table, ignoring.", raceMask);
4083 continue;
4084 }
4085
4086 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4087 {
4088 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_skills` table, ignoring.", classMask);
4089 continue;
4090 }
4091
4092 if (!sSkillLineStore.LookupEntry(skill.SkillId))
4093 {
4094 TC_LOG_ERROR("sql.sql", "Wrong skill id {} in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
4095 continue;
4096 }
4097
4098 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4099 {
4100 if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
4101 {
4102 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4103 {
4104 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4105 {
4106 if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
4107 continue;
4108
4109 if (auto& info = _playerInfo[raceIndex][classIndex])
4110 {
4111 info->skills.push_back(skill);
4112 ++count;
4113 }
4114 }
4115 }
4116 }
4117 }
4118 } while (result->NextRow());
4119
4120 TC_LOG_INFO("server.loading", ">> Loaded {} player create skills in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4121 }
4122 }
4123
4124 // Load playercreate spells
4125 TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
4126 {
4127 uint32 oldMSTime = getMSTime();
4128
4129 QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
4130
4131 if (!result)
4132 {
4133 TC_LOG_INFO("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
4134 }
4135 else
4136 {
4137 uint32 count = 0;
4138
4139 do
4140 {
4141 Field* fields = result->Fetch();
4142 uint32 raceMask = fields[0].GetUInt32();
4143 uint32 classMask = fields[1].GetUInt32();
4144 uint32 spellId = fields[2].GetUInt32();
4145
4146 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
4147 {
4148 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
4149 continue;
4150 }
4151
4152 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4153 {
4154 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_spell_custom` table, ignoring.", classMask);
4155 continue;
4156 }
4157
4158 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4159 {
4160 if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
4161 {
4162 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4163 {
4164 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4165 {
4166 if (auto& info = _playerInfo[raceIndex][classIndex])
4167 {
4168 info->customSpells.push_back(spellId);
4169 ++count;
4170 }
4171 // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
4172 // Either split the masks per class, or per race, which kind of kills the point yet.
4173 // else if (raceMask != 0 && classMask != 0)
4174 // 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);
4175 }
4176 }
4177 }
4178 }
4179 }
4180 while (result->NextRow());
4181
4182 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4183 }
4184 }
4185
4186 // Load playercreate cast spell
4187 TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
4188 {
4189 uint32 oldMSTime = getMSTime();
4190
4191 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell");
4192
4193 if (!result)
4194 TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
4195 else
4196 {
4197 uint32 count = 0;
4198
4199 do
4200 {
4201 Field* fields = result->Fetch();
4202 uint32 raceMask = fields[0].GetUInt32();
4203 uint32 classMask = fields[1].GetUInt32();
4204 uint32 spellId = fields[2].GetUInt32();
4205
4206 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
4207 {
4208 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_cast_spell` table, ignoring.", raceMask);
4209 continue;
4210 }
4211
4212 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4213 {
4214 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_cast_spell` table, ignoring.", classMask);
4215 continue;
4216 }
4217
4218 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4219 {
4220 if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
4221 {
4222 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4223 {
4224 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4225 {
4226 if (auto& info = _playerInfo[raceIndex][classIndex])
4227 {
4228 info->castSpells.push_back(spellId);
4229 ++count;
4230 }
4231 }
4232 }
4233 }
4234 }
4235 } while (result->NextRow());
4236
4237 TC_LOG_INFO("server.loading", ">> Loaded {} player create cast spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4238 }
4239 }
4240
4241 // Load playercreate actions
4242 TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
4243 {
4244 uint32 oldMSTime = getMSTime();
4245
4246 // 0 1 2 3 4
4247 QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
4248
4249 if (!result)
4250 {
4251 TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
4252 }
4253 else
4254 {
4255 uint32 count = 0;
4256
4257 do
4258 {
4259 Field* fields = result->Fetch();
4260
4261 uint32 current_race = fields[0].GetUInt8();
4262 if (current_race >= MAX_RACES)
4263 {
4264 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_action` table, ignoring.", current_race);
4265 continue;
4266 }
4267
4268 uint32 current_class = fields[1].GetUInt8();
4269 if (current_class >= MAX_CLASSES)
4270 {
4271 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_action` table, ignoring.", current_class);
4272 continue;
4273 }
4274
4275 if (auto& info = _playerInfo[current_race][current_class])
4276 info->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
4277
4278 ++count;
4279 }
4280 while (result->NextRow());
4281
4282 TC_LOG_INFO("server.loading", ">> Loaded {} player create actions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4283 }
4284 }
4285
4286 // Loading levels data (class only dependent)
4287 TC_LOG_INFO("server.loading", "Loading Player Create Level HP/Mana Data...");
4288 {
4289 uint32 oldMSTime = getMSTime();
4290
4291 // 0 1 2 3
4292 QueryResult result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
4293
4294 if (!result)
4295 {
4296 TC_LOG_ERROR("server.loading", ">> Loaded 0 level health/mana definitions. DB table `player_classlevelstats` is empty.");
4297 ABORT();
4298 }
4299
4300 uint32 count = 0;
4301
4302 do
4303 {
4304 Field* fields = result->Fetch();
4305
4306 uint32 current_class = fields[0].GetUInt8();
4307 if (current_class >= MAX_CLASSES)
4308 {
4309 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_classlevelstats` table, ignoring.", current_class);
4310 continue;
4311 }
4312
4313 uint8 current_level = fields[1].GetUInt8(); // Can't be > than STRONG_MAX_LEVEL (hardcoded level maximum) due to var type
4314 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4315 {
4316 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_classlevelstats` table, ignoring.", current_level);
4317 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4318 continue;
4319 }
4320
4321 auto& info = _playerClassInfo[current_class];
4322 if (!info)
4323 {
4324 info = std::make_unique<PlayerClassInfo>();
4325 info->levelInfo = std::make_unique<PlayerClassLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4326 }
4327
4328 PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level - 1];
4329 levelInfo.basehealth = fields[2].GetUInt16();
4330 levelInfo.basemana = fields[3].GetUInt16();
4331
4332 ++count;
4333 }
4334 while (result->NextRow());
4335
4336 // Fill gaps and check integrity
4337 for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
4338 {
4339 // skip non existed classes
4340 if (!sChrClassesStore.LookupEntry(class_))
4341 continue;
4342
4343 auto& pClassInfo = _playerClassInfo[class_];
4344
4345 // fatal error if no level 1 data
4346 if (!pClassInfo || !pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0)
4347 {
4348 TC_LOG_ERROR("sql.sql", "Class {} Level 1 does not have health/mana data!", class_);
4349 ABORT();
4350 }
4351
4352 // fill level gaps
4353 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4354 {
4355 if (pClassInfo->levelInfo[level].basehealth == 0)
4356 {
4357 TC_LOG_ERROR("sql.sql", "Class {} Level {} does not have health/mana data. Using stats data of level {}.", class_, level + 1, level);
4358 pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
4359 }
4360 }
4361 }
4362
4363 TC_LOG_INFO("server.loading", ">> Loaded {} level health/mana definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4364 }
4365
4366 // Loading levels data (class/race dependent)
4367 TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
4368 {
4369 uint32 oldMSTime = getMSTime();
4370
4371 // 0 1 2 3 4 5 6 7
4372 QueryResult result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
4373
4374 if (!result)
4375 {
4376 TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_levelstats` is empty.");
4377 ABORT();
4378 }
4379
4380 uint32 count = 0;
4381
4382 do
4383 {
4384 Field* fields = result->Fetch();
4385
4386 uint32 current_race = fields[0].GetUInt8();
4387 if (current_race >= MAX_RACES)
4388 {
4389 TC_LOG_ERROR("sql.sql", "Wrong race {} in `player_levelstats` table, ignoring.", current_race);
4390 continue;
4391 }
4392
4393 uint32 current_class = fields[1].GetUInt8();
4394 if (current_class >= MAX_CLASSES)
4395 {
4396 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_levelstats` table, ignoring.", current_class);
4397 continue;
4398 }
4399
4400 uint32 current_level = fields[2].GetUInt8();
4401 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4402 {
4403 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4404 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4405 else
4406 {
4407 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_levelstats` table, ignoring.", current_level);
4408 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4409 }
4410 continue;
4411 }
4412
4413 if (auto& info = _playerInfo[current_race][current_class])
4414 {
4415 if (!info->levelInfo)
4416 info->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4417
4418 PlayerLevelInfo& levelInfo = info->levelInfo[current_level - 1];
4419 for (uint8 i = 0; i < MAX_STATS; ++i)
4420 levelInfo.stats[i] = fields[i + 3].GetUInt16();
4421 }
4422
4423 ++count;
4424 }
4425 while (result->NextRow());
4426
4427 // Fill gaps and check integrity
4428 for (uint8 race = 0; race < MAX_RACES; ++race)
4429 {
4430 // skip non existed races
4431 if (!sChrRacesStore.LookupEntry(race))
4432 continue;
4433
4434 for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
4435 {
4436 // skip non existed classes
4437 if (!sChrClassesStore.LookupEntry(class_))
4438 continue;
4439
4440 auto& info = _playerInfo[race][class_];
4441 if (!info)
4442 continue;
4443
4444 // skip expansion races if not playing with expansion
4445 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
4446 continue;
4447
4448 // skip expansion classes if not playing with expansion
4450 continue;
4451
4452 // fatal error if no level 1 data
4453 if (!info->levelInfo || info->levelInfo[0].stats[0] == 0)
4454 {
4455 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level 1 does not have stats data!", race, class_);
4456 ABORT();
4457 }
4458
4459 // fill level gaps
4460 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4461 {
4462 if (info->levelInfo[level].stats[0] == 0)
4463 {
4464 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level {} does not have stats data. Using stats data of level {}.", race, class_, level + 1, level);
4465 info->levelInfo[level] = info->levelInfo[level - 1];
4466 }
4467 }
4468 }
4469 }
4470
4471 TC_LOG_INFO("server.loading", ">> Loaded {} level stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4472 }
4473
4474 // Loading xp per level data
4475 TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
4476 {
4477 uint32 oldMSTime = getMSTime();
4478
4479 _playerXPperLevel.resize(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4480 for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4481 _playerXPperLevel[level] = 0;
4482
4483 // 0 1
4484 QueryResult result = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level");
4485
4486 if (!result)
4487 {
4488 TC_LOG_ERROR("server.loading", ">> Loaded 0 xp for level definitions. DB table `player_xp_for_level` is empty.");
4489 ABORT();
4490 }
4491
4492 uint32 count = 0;
4493
4494 do
4495 {
4496 Field* fields = result->Fetch();
4497
4498 uint32 current_level = fields[0].GetUInt8();
4499 uint32 current_xp = fields[1].GetUInt32();
4500
4501 if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4502 {
4503 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4504 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4505 else
4506 {
4507 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_xp_for_levels` table, ignoring.", current_level);
4508 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4509 }
4510 continue;
4511 }
4512 //PlayerXPperLevel
4513 _playerXPperLevel[current_level] = current_xp;
4514 ++count;
4515 }
4516 while (result->NextRow());
4517
4518 // fill level gaps
4519 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4520 {
4521 if (_playerXPperLevel[level] == 0)
4522 {
4523 TC_LOG_ERROR("sql.sql", "Level {} does not have XP for level data. Using data of level [{}] + 100.", level + 1, level);
4524 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 100;
4525 }
4526 }
4527
4528 TC_LOG_INFO("server.loading", ">> Loaded {} xp for level definition(s) from database in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4529 }
4530}
4531
4533{
4534 if (level < 1 || class_ >= MAX_CLASSES)
4535 return;
4536
4537 auto const& pInfo = _playerClassInfo[class_];
4538
4539 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4540 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
4541
4542 *info = pInfo->levelInfo[level - 1];
4543}
4544
4546{
4547 if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
4548 return;
4549
4550 auto const& pInfo = _playerInfo[race][class_];
4551 if (!pInfo)
4552 return;
4553
4554 if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4555 *info = pInfo->levelInfo[level - 1];
4556 else
4557 BuildPlayerLevelInfo(race, class_, level, info);
4558}
4559
4561{
4562 // base data (last known level)
4563 *info = _playerInfo[race][_class]->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
4564
4565 // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
4566 for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
4567 {
4568 switch (_class)
4569 {
4570 case CLASS_WARRIOR:
4571 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4572 info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4573 info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4574 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4575 info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4576 break;
4577 case CLASS_PALADIN:
4578 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
4579 info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4580 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
4581 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
4582 info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
4583 break;
4584 case CLASS_HUNTER:
4585 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
4586 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4587 info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4588 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
4589 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
4590 break;
4591 case CLASS_ROGUE:
4592 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
4593 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4594 info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
4595 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
4596 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
4597 break;
4598 case CLASS_PRIEST:
4599 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4600 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4601 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
4602 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
4603 info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
4604 break;
4605 case CLASS_SHAMAN:
4606 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4607 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4608 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
4609 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
4610 info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
4611 break;
4612 case CLASS_MAGE:
4613 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4614 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4615 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4616 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
4617 info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4618 break;
4619 case CLASS_WARLOCK:
4620 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4621 info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4622 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4623 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4624 info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4625 break;
4626 case CLASS_DRUID:
4627 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
4628 info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
4629 info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
4630 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
4631 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
4632 }
4633 }
4634}
4635
4637{
4638 uint32 oldMSTime = getMSTime();
4639
4640 _questTemplates.clear();
4641
4642 _exclusiveQuestGroups.clear();
4643
4644 QueryResult result = WorldDatabase.Query("SELECT "
4645 //0 1 2 3 4 5 6 7 8
4646 "ID, QuestType, QuestLevel, MinLevel, QuestSortID, QuestInfoID, SuggestedGroupNum, TimeAllowed, AllowableRaces,"
4647 // 9 10 11 12
4648 "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, "
4649 // 13 14 15 16 17 18 19 20
4650 "RewardNextQuest, RewardXPDifficulty, RewardMoney, RewardBonusMoney, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
4651 // 21 22 23 24 25 26
4652 "StartItem, Flags, RewardTitle, RequiredPlayerKills, RewardTalents, RewardArenaPoints, "
4653 // 27 28 29 30 31 32 33 34
4654 "RewardItem1, RewardAmount1, RewardItem2, RewardAmount2, RewardItem3, RewardAmount3, RewardItem4, RewardAmount4, "
4655 // 35 36 37 38 39 40 41 42 43 44 45 46
4656 "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemID6, RewardChoiceItemQuantity6, "
4657 // 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
4658 "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionID5, RewardFactionValue5, RewardFactionOverride5,"
4659 // 62 63 64 65
4660 "POIContinent, POIx, POIy, POIPriority, "
4661 // 66 67 68 69 70
4662 "LogTitle, LogDescription, QuestDescription, AreaDescription, QuestCompletionLog, "
4663 // 71 72 73 74 75 76 77 78
4664 "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
4665 // 79 80 81 82 83 84 85 86
4666 "ItemDrop1, ItemDrop2, ItemDrop3, ItemDrop4, ItemDropQuantity1, ItemDropQuantity2, ItemDropQuantity3, ItemDropQuantity4, "
4667 // 87 88 89 90 91 92 93 94 95 96 97 98
4668 "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
4669 // 99 100 101 102 103
4670 "RewardFactionFlags, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4"
4671 " FROM quest_template");
4672 if (!result)
4673 {
4674 TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
4675 return;
4676 }
4677
4678 _questTemplates.reserve(result->GetRowCount());
4679
4680 // create multimap previous quest for each existed quest
4681 // some quests can have many previous maps set by NextQuestId in previous quest
4682 // for example set of race quests can lead to single not race specific quest
4683 do
4684 {
4685 Field* fields = result->Fetch();
4686
4687 uint32 questId = fields[0].GetUInt32();
4688 auto itr = _questTemplates.emplace(std::piecewise_construct, std::forward_as_tuple(questId), std::forward_as_tuple(new Quest(fields))).first;
4689 itr->second->_weakRef = itr->second;
4690 } while (result->NextRow());
4691
4692 std::unordered_map<uint32, uint32> usedMailTemplates;
4693
4694 struct QuestLoaderHelper
4695 {
4696 typedef void(Quest::*QuestLoaderFunction)(Field* fields);
4697
4698 char const* QueryFields;
4699 char const* TableName;
4700 char const* TableDesc;
4701 QuestLoaderFunction LoaderFunction;
4702 };
4703
4704 static std::vector<QuestLoaderHelper> const QuestLoaderHelpers =
4705 {
4706 // 0 1 2 3 4 5 6 7 8
4707 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4", "quest_details", "details", &Quest::LoadQuestDetails },
4708
4709 // 0 1 2 3 4 5
4710 { "ID, EmoteOnComplete, EmoteOnIncomplete, EmoteOnCompleteDelay, EmoteOnIncompleteDelay, CompletionText", "quest_request_items", "request items", &Quest::LoadQuestRequestItems },
4711
4712 // 0 1 2 3 4 5 6 7 8 9
4713 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText", "quest_offer_reward", "reward emotes", &Quest::LoadQuestOfferReward },
4714
4715 // 0 1 2 3 4 5 6 7 8 9
4716 { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
4717 // 10 11 12 13 14 15 16 17
4718 " RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags", "quest_template_addon", "template addons", &Quest::LoadQuestTemplateAddon },
4719
4720 // 0 1
4721 { "QuestId, RewardMailSenderEntry", "quest_mail_sender", "mail sender entries", &Quest::LoadQuestMailSender }
4722 };
4723
4724 for (QuestLoaderHelper const& loader : QuestLoaderHelpers)
4725 {
4726 result = WorldDatabase.PQuery("SELECT {} FROM {}", loader.QueryFields, loader.TableName);
4727 if (!result)
4728 TC_LOG_INFO("server.loading", ">> Loaded 0 quest {}. DB table `{}` is empty.", loader.TableDesc, loader.TableName);
4729 else
4730 {
4731 do
4732 {
4733 Field* fields = result->Fetch();
4734 uint32 questId = fields[0].GetUInt32();
4735
4736 auto itr = _questTemplates.find(questId);
4737 if (itr != _questTemplates.end())
4738 (itr->second.get()->*loader.LoaderFunction)(fields);
4739 else
4740 TC_LOG_ERROR("server.loading", "Table `{}` has data for quest {} but such quest does not exist", loader.TableName, questId);
4741 } while (result->NextRow());
4742 }
4743 }
4744
4745 // Post processing
4746 for (auto& questPair : _questTemplates)
4747 {
4748 // skip post-loading checks for disabled quests
4749 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
4750 continue;
4751
4752 Quest* qinfo = questPair.second.get();
4753
4754 // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
4755
4757 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestType` = {}, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestType());
4758
4760 {
4761 TC_LOG_ERROR("sql.sql", "Quest {} has `SpecialFlags` = {} > max allowed value. Correct `SpecialFlags` to value <= {}",
4764 }
4765
4766 if (qinfo->_flags & QUEST_FLAGS_DAILY && qinfo->_flags & QUEST_FLAGS_WEEKLY)
4767 {
4768 TC_LOG_ERROR("sql.sql", "Weekly Quest {} is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
4769 qinfo->_flags &= ~QUEST_FLAGS_DAILY;
4770 }
4771
4772 if (qinfo->_flags & QUEST_FLAGS_DAILY)
4773 {
4775 {
4776 TC_LOG_ERROR("sql.sql", "Daily Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4778 }
4779 }
4780
4781 if (qinfo->_flags & QUEST_FLAGS_WEEKLY)
4782 {
4784 {
4785 TC_LOG_ERROR("sql.sql", "Weekly Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4787 }
4788 }
4789
4791 {
4793 {
4794 TC_LOG_ERROR("sql.sql", "Monthly quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4796 }
4797 }
4798
4799 if (qinfo->_flags & QUEST_FLAGS_TRACKING)
4800 {
4801 // at auto-reward can be rewarded only RewardChoiceItemId[0]
4802 for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
4803 {
4804 if (uint32 id = qinfo->RewardChoiceItemId[j])
4805 {
4806 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item from `RewardChoiceItemId{}` can't be rewarded with quest flag QUEST_FLAGS_TRACKING.",
4807 qinfo->GetQuestId(), j + 1, id, j + 1);
4808 // no changes, quest ignore this data
4809 }
4810 }
4811 }
4812
4813 // client quest log visual (area case)
4814 if (qinfo->_zoneOrSort > 0)
4815 {
4816 if (!sAreaTableStore.LookupEntry(qinfo->_zoneOrSort))
4817 {
4818 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (zone case) but zone with this id does not exist.",
4819 qinfo->GetQuestId(), qinfo->_zoneOrSort);
4820 // no changes, quest not dependent from this value but can have problems at client
4821 }
4822 }
4823 // client quest log visual (sort case)
4824 if (qinfo->_zoneOrSort < 0)
4825 {
4826 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->_zoneOrSort));
4827 if (!qSort)
4828 {
4829 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} (sort case) but quest sort with this id does not exist.",
4830 qinfo->GetQuestId(), qinfo->_zoneOrSort);
4831 // 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)
4832 }
4833 //check for proper RequiredSkillId value (skill case)
4834 if (uint32 skill_id = SkillByQuestSort(-qinfo->_zoneOrSort))
4835 {
4836 if (qinfo->_requiredSkillId != skill_id)
4837 {
4838 TC_LOG_ERROR("sql.sql", "Quest {} has `QuestSortID` = {} but `RequiredSkillId` does not have a corresponding value ({}).",
4839 qinfo->GetQuestId(), qinfo->_zoneOrSort, skill_id);
4840 //override, and force proper value here?
4841 }
4842 }
4843 }
4844
4845 // AllowableClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
4846 if (qinfo->_allowableClasses)
4847 {
4849 {
4850 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable classes in `AllowableClasses` ({}), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->_allowableClasses);
4851 qinfo->_allowableClasses = 0;
4852 }
4853 }
4854
4855 // AllowableRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
4856 if (qinfo->_allowableRaces)
4857 {
4858 if (!(qinfo->_allowableRaces & RACEMASK_ALL_PLAYABLE))
4859 {
4860 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable races in `AllowableRaces` ({}), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->_allowableRaces);
4861 qinfo->_allowableRaces = 0;
4862 }
4863 }
4864
4865 // RequiredSkillId, can be 0
4866 if (qinfo->_requiredSkillId)
4867 {
4868 if (!sSkillLineStore.LookupEntry(qinfo->_requiredSkillId))
4869 {
4870 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillId` = {} but this skill does not exist",
4871 qinfo->GetQuestId(), qinfo->_requiredSkillId);
4872 }
4873 }
4874
4875 if (qinfo->_requiredSkillPoints)
4876 {
4877 if (qinfo->_requiredSkillPoints > sWorld->GetConfigMaxSkillValue())
4878 {
4879 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillPoints` = {} but max possible skill is {}, quest can't be done.",
4880 qinfo->GetQuestId(), qinfo->_requiredSkillPoints, sWorld->GetConfigMaxSkillValue());
4881 // no changes, quest can't be done for this requirement
4882 }
4883 }
4884 // else Skill quests can have 0 skill level, this is ok
4885
4886 if (qinfo->_requiredFactionId2 && !sFactionStore.LookupEntry(qinfo->_requiredFactionId2))
4887 {
4888 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionId2` = {} but faction template {} does not exist, quest can't be done.",
4889 qinfo->GetQuestId(), qinfo->_requiredFactionId2, qinfo->_requiredFactionId2);
4890 // no changes, quest can't be done for this requirement
4891 }
4892
4893 if (qinfo->_requiredFactionId1 && !sFactionStore.LookupEntry(qinfo->_requiredFactionId1))
4894 {
4895 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionId1` = {} but faction template {} does not exist, quest can't be done.",
4896 qinfo->GetQuestId(), qinfo->_requiredFactionId1, qinfo->_requiredFactionId1);
4897 // no changes, quest can't be done for this requirement
4898 }
4899
4900 if (qinfo->_requiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMinRepFaction))
4901 {
4902 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4904 // no changes, quest can't be done for this requirement
4905 }
4906
4907 if (qinfo->_requiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMaxRepFaction))
4908 {
4909 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4911 // no changes, quest can't be done for this requirement
4912 }
4913
4915 {
4916 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but max reputation is {}, quest can't be done.",
4918 // no changes, quest can't be done for this requirement
4919 }
4920
4922 {
4923 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} and `RequiredMinRepValue` = {}, quest can't be done.",
4924 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue, qinfo->_requiredMinRepValue);
4925 // no changes, quest can't be done for this requirement
4926 }
4927
4928 if (!qinfo->_requiredFactionId1 && qinfo->_requiredFactionValue1 != 0)
4929 {
4930 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionValue1` = {} but `RequiredFactionId1` is 0, value has no effect",
4931 qinfo->GetQuestId(), qinfo->_requiredFactionValue1);
4932 // warning
4933 }
4934
4935 if (!qinfo->_requiredFactionId2 && qinfo->_requiredFactionValue2 != 0)
4936 {
4937 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionValue2` = {} but `RequiredFactionId2` is 0, value has no effect",
4938 qinfo->GetQuestId(), qinfo->_requiredFactionValue2);
4939 // warning
4940 }
4941
4942 if (!qinfo->_requiredMinRepFaction && qinfo->_requiredMinRepValue != 0)
4943 {
4944 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but `RequiredMinRepFaction` is 0, value has no effect",
4945 qinfo->GetQuestId(), qinfo->_requiredMinRepValue);
4946 // warning
4947 }
4948
4949 if (!qinfo->_requiredMaxRepFaction && qinfo->_requiredMaxRepValue != 0)
4950 {
4951 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} but `RequiredMaxRepFaction` is 0, value has no effect",
4952 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue);
4953 // warning
4954 }
4955
4956 if (qinfo->_rewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->_rewardTitleId))
4957 {
4958 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardTitleId` = {} but CharTitle Id {} does not exist, quest can't be rewarded with title.",
4959 qinfo->GetQuestId(), qinfo->GetRewTitle(), qinfo->GetRewTitle());
4960 qinfo->_rewardTitleId = 0;
4961 // quest can't reward this title
4962 }
4963
4964 if (qinfo->_sourceItemId)
4965 {
4966 if (!sObjectMgr->GetItemTemplate(qinfo->_sourceItemId))
4967 {
4968 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but item with entry {} does not exist, quest can't be done.",
4969 qinfo->GetQuestId(), qinfo->_sourceItemId, qinfo->_sourceItemId);
4970 qinfo->_sourceItemId = 0; // quest can't be done for this requirement
4971 }
4972 else if (qinfo->_sourceItemIdCount == 0)
4973 {
4974 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but `StartItemCount` = 0, set to 1 but need fix in DB.",
4975 qinfo->GetQuestId(), qinfo->_sourceItemId);
4976 qinfo->_sourceItemIdCount = 1; // update to 1 for allow quest work for backward compatibility with DB
4977 }
4978 }
4979 else if (qinfo->_sourceItemIdCount > 0)
4980 {
4981 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = 0 but `StartItemCount` = {}, useless value.",
4982 qinfo->GetQuestId(), qinfo->_sourceItemIdCount);
4983 qinfo->_sourceItemIdCount = 0; // no quest work changes in fact
4984 }
4985
4986 if (qinfo->_sourceSpellID)
4987 {
4988 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_sourceSpellID);
4989 if (!spellInfo)
4990 {
4991 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} doesn't exist, quest can't be done.",
4992 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4993 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
4994 }
4995 else if (!SpellMgr::IsSpellValid(spellInfo))
4996 {
4997 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} is broken, quest can't be done.",
4998 qinfo->GetQuestId(), qinfo->_sourceSpellID, qinfo->_sourceSpellID);
4999 qinfo->_sourceSpellID = 0; // quest can't be done for this requirement
5000 }
5001 }
5002
5003 for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
5004 {
5005 uint32 id = qinfo->RequiredItemId[j];
5006 if (id)
5007 {
5008 if (qinfo->RequiredItemCount[j] == 0)
5009 {
5010 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but `RequiredItemCount{}` = 0, quest can't be done.",
5011 qinfo->GetQuestId(), j+1, id, j+1);
5012 // no changes, quest can't be done for this requirement
5013 }
5014
5016
5017 if (!sObjectMgr->GetItemTemplate(id))
5018 {
5019 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but item with entry {} does not exist, quest can't be done.",
5020 qinfo->GetQuestId(), j+1, id, id);
5021 qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest
5022 }
5023 }
5024 else if (qinfo->RequiredItemCount[j]>0)
5025 {
5026 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = 0 but `RequiredItemCount{}` = {}, quest can't be done.",
5027 qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredItemCount[j]);
5028 qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest
5029 }
5030 }
5031
5032 for (uint8 j = 0; j < QUEST_ITEM_DROP_COUNT; ++j)
5033 {
5034 uint32 id = qinfo->ItemDrop[j];
5035 if (id)
5036 {
5037 if (!sObjectMgr->GetItemTemplate(id))
5038 {
5039 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
5040 qinfo->GetQuestId(), j+1, id, id);
5041 // no changes, quest can't be done for this requirement
5042 }
5043 }
5044 else
5045 {
5046 if (qinfo->ItemDropQuantity[j]>0)
5047 {
5048 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = 0 but `ItemDropQuantity{}` = {}.",
5049 qinfo->GetQuestId(), j+1, j+1, qinfo->ItemDropQuantity[j]);
5050 // no changes, quest ignore this data
5051 }
5052 }
5053 }
5054
5055 for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
5056 {
5057 int32 id = qinfo->RequiredNpcOrGo[j];
5058 if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
5059 {
5060 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but gameobject {} does not exist, quest can't be done.",
5061 qinfo->GetQuestId(), j+1, id, uint32(-id));
5062 qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
5063 }
5064
5065 if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
5066 {
5067 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but creature with entry {} does not exist, quest can't be done.",
5068 qinfo->GetQuestId(), j+1, id, uint32(id));
5069 qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
5070 }
5071
5072 if (id)
5073 {
5074 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
5075
5077
5078 if (!qinfo->RequiredNpcOrGoCount[j])
5079 {
5080 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but `RequiredNpcOrGoCount{}` = 0, quest can't be done.",
5081 qinfo->GetQuestId(), j+1, id, j+1);
5082 // no changes, quest can be incorrectly done, but we already report this
5083 }
5084 }
5085 else if (qinfo->RequiredNpcOrGoCount[j]>0)
5086 {
5087 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = 0 but `RequiredNpcOrGoCount{}` = {}.",
5088 qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredNpcOrGoCount[j]);
5089 // no changes, quest ignore this data
5090 }
5091 }
5092
5093 for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
5094 {
5095 uint32 id = qinfo->RewardChoiceItemId[j];
5096 if (id)
5097 {
5098 if (!sObjectMgr->GetItemTemplate(id))
5099 {
5100 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5101 qinfo->GetQuestId(), j+1, id, id);
5102 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5103 }
5104
5105 if (!qinfo->RewardChoiceItemCount[j])
5106 {
5107 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but `RewardChoiceItemCount{}` = 0, quest can't be done.",
5108 qinfo->GetQuestId(), j+1, id, j+1);
5109 // no changes, quest can't be done
5110 }
5111 }
5112 else if (qinfo->RewardChoiceItemCount[j]>0)
5113 {
5114 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = 0 but `RewardChoiceItemCount{}` = {}.",
5115 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardChoiceItemCount[j]);
5116 // no changes, quest ignore this data
5117 }
5118 }
5119
5120 for (uint8 j = 0; j < QUEST_REWARD_ITEM_COUNT; ++j)
5121 {
5122 uint32 id = qinfo->RewardItemId[j];
5123 if (id)
5124 {
5125 if (!sObjectMgr->GetItemTemplate(id))
5126 {
5127 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5128 qinfo->GetQuestId(), j+1, id, id);
5129 qinfo->RewardItemId[j] = 0; // no changes, quest will not reward this item
5130 }
5131
5132 if (!qinfo->RewardItemCount[j])
5133 {
5134 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but `RewardItemCount{}` = 0, quest will not reward this item.",
5135 qinfo->GetQuestId(), j+1, id, j+1);
5136 // no changes
5137 }
5138 }
5139 else if (qinfo->RewardItemCount[j]>0)
5140 {
5141 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = 0 but `RewardItemCount{}` = {}.",
5142 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemCount[j]);
5143 // no changes, quest ignore this data
5144 }
5145 }
5146
5147 for (uint8 j = 0; j < QUEST_REWARD_REPUTATIONS_COUNT; ++j)
5148 {
5149 if (qinfo->RewardFactionId[j])
5150 {
5151 if (std::abs(qinfo->RewardFactionValue[j]) > 9)
5152 {
5153 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]);
5154 }
5155 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
5156 {
5157 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]);
5158 qinfo->RewardFactionId[j] = 0; // quest will not reward this
5159 }
5160 }
5161
5162 else if (qinfo->RewardFactionOverride[j] != 0)
5163 {
5164 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardFactionId{}` = 0 but `RewardFactionValueIdOverride{}` = {}.",
5165 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionOverride[j]);
5166 // no changes, quest ignore this data
5167 }
5168 }
5169
5170 if (qinfo->_rewardDisplaySpell)
5171 {
5172 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardDisplaySpell);
5173 if (!spellInfo)
5174 {
5175 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} does not exist, spell removed as display reward.",
5176 qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
5177 qinfo->_rewardDisplaySpell = 0; // no spell reward will display for this quest
5178 }
5179
5180 else if (!SpellMgr::IsSpellValid(spellInfo))
5181 {
5182 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} is broken, quest will not have a spell reward.",
5183 qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
5184 qinfo->_rewardDisplaySpell = 0; // no spell reward will display for this quest
5185 }
5186
5187 else if (GetTalentSpellCost(qinfo->_rewardDisplaySpell))
5188 {
5189 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} is talent, quest will not have a spell reward.",
5190 qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
5191 qinfo->_rewardDisplaySpell = 0; // no spell reward will display for this quest
5192 }
5193 }
5194
5195 if (qinfo->_rewardSpell > 0)
5196 {
5197 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardSpell);
5198 if (!spellInfo)
5199 {
5200 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpell` = {} but spell {} does not exist, quest will not have a spell reward.",
5201 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5202 qinfo->_rewardSpell = 0; // no spell will be cast on player
5203 }
5204
5205 else if (!SpellMgr::IsSpellValid(spellInfo))
5206 {
5207 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpell` = {} but spell {} is broken, quest will not have a spell reward.",
5208 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5209 qinfo->_rewardSpell = 0; // no spell will be cast on player
5210 }
5211
5212 else if (GetTalentSpellCost(qinfo->_rewardSpell))
5213 {
5214 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} is talent, quest will not have a spell reward.",
5215 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5216 qinfo->_rewardSpell = 0; // no spell will be cast on player
5217 }
5218 }
5219
5220 if (qinfo->_rewardMailTemplateId)
5221 {
5222 if (!sMailTemplateStore.LookupEntry(qinfo->_rewardMailTemplateId))
5223 {
5224 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} does not exist, quest will not have a mail reward.",
5225 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId);
5226 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5227 qinfo->_rewardMailDelay = 0; // no mail will send to player
5228 qinfo->_rewardMailSenderEntry = 0;
5229 }
5230 else if (usedMailTemplates.find(qinfo->_rewardMailTemplateId) != usedMailTemplates.end())
5231 {
5232 auto used_mt_itr = usedMailTemplates.find(qinfo->_rewardMailTemplateId);
5233 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} already used for quest {}, quest will not have a mail reward.",
5234 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId, used_mt_itr->second);
5235 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5236 qinfo->_rewardMailDelay = 0; // no mail will send to player
5237 qinfo->_rewardMailSenderEntry = 0;
5238 }
5239 else
5240 usedMailTemplates.emplace(qinfo->_rewardMailTemplateId, qinfo->GetQuestId());
5241 }
5242
5243 if (uint32 rewardNextQuest = qinfo->_rewardNextQuest)
5244 {
5245 if (!_questTemplates.count(rewardNextQuest))
5246 {
5247 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardNextQuest` = {} but quest {} does not exist, quest chain will not work.",
5248 qinfo->GetQuestId(), qinfo->_rewardNextQuest, qinfo->_rewardNextQuest);
5249 qinfo->_rewardNextQuest = 0;
5250 }
5251 }
5252
5253 // fill additional data stores
5254 if (uint32 prevQuestId = std::abs(qinfo->_prevQuestID))
5255 {
5256 auto prevQuestItr = _questTemplates.find(prevQuestId);
5257 if (prevQuestItr == _questTemplates.end())
5258 TC_LOG_ERROR("sql.sql", "Quest {} has PrevQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_prevQuestID);
5259 else if (prevQuestItr->second->_breadcrumbForQuestId)
5260 TC_LOG_ERROR("sql.sql", "Quest {} should not be unlocked by breadcrumb quest {}", qinfo->_id, prevQuestId);
5261 else if (qinfo->_prevQuestID > 0)
5262 qinfo->DependentPreviousQuests.push_back(prevQuestId);
5263 }
5264
5265 if (uint32 nextQuestId = qinfo->_nextQuestID)
5266 {
5267 auto nextQuestItr = _questTemplates.find(nextQuestId);
5268 if (nextQuestItr == _questTemplates.end())
5269 TC_LOG_ERROR("sql.sql", "Quest {} has NextQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestID);
5270 else
5271 nextQuestItr->second->DependentPreviousQuests.push_back(qinfo->GetQuestId());
5272 }
5273
5274 if (uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId))
5275 {
5276 if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
5277 {
5278 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb for quest {}, but no such quest exists", qinfo->_id, breadcrumbForQuestId);
5279 qinfo->_breadcrumbForQuestId = 0;
5280 }
5281 if (qinfo->_nextQuestID)
5282 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb, should not unlock quest {}", qinfo->_id, qinfo->_nextQuestID);
5283 }
5284
5285 if (qinfo->_exclusiveGroup)
5286 _exclusiveQuestGroups.emplace(qinfo->_exclusiveGroup, qinfo->GetQuestId());
5287 if (qinfo->_limitTime)
5289 if (qinfo->_requiredPlayerKills)
5291
5292 // Special flag to determine if quest is completed from the start, used to determine if we can fail timed quest if it is completed
5294 {
5295 bool addFlag = true;
5297 {
5298 for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
5299 {
5300 if (qinfo->RequiredItemId[j] != 0 && (qinfo->RequiredItemId[j] != qinfo->GetSrcItemId() || qinfo->RequiredItemCount[j] > qinfo->GetSrcItemCount()))
5301 {
5302 addFlag = false;
5303 break;
5304 }
5305 }
5306 }
5307
5308 if (addFlag)
5310 }
5311 }
5312
5313 // Disallow any breadcrumb loops and inform quests of their breadcrumbs
5314 for (auto& questPair : _questTemplates)
5315 {
5316 // skip post-loading checks for disabled quests
5317 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
5318 continue;
5319
5320 Quest* qinfo = questPair.second.get();
5321 uint32 qid = qinfo->GetQuestId();
5322 uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId);
5323 std::set<uint32> questSet;
5324
5325 while(breadcrumbForQuestId)
5326 {
5327 //a previously visited quest was found as a breadcrumb quest
5328 //breadcrumb loop found!
5329 if (!questSet.insert(qinfo->_id).second)
5330 {
5331 TC_LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} are in a loop", qid, breadcrumbForQuestId);
5332 qinfo->_breadcrumbForQuestId = 0;
5333 break;
5334 }
5335
5336 qinfo = const_cast<Quest*>(sObjectMgr->GetQuestTemplate(breadcrumbForQuestId));
5337
5338 //every quest has a list of every breadcrumb towards it
5339 qinfo->DependentBreadcrumbQuests.push_back(qid);
5340
5341 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId();
5342 }
5343 }
5344
5345 // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
5346 for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
5347 {
5348 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
5349 if (!spellInfo)
5350 continue;
5351
5352 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
5353 {
5354 if (spellEffectInfo.Effect != SPELL_EFFECT_QUEST_COMPLETE)
5355 continue;
5356
5357 uint32 quest_id = spellEffectInfo.MiscValue;
5358
5359 Quest const* quest = GetQuestTemplate(quest_id);
5360
5361 // some quest referenced in spells not exist (outdated spells)
5362 if (!quest)
5363 continue;
5364
5366 {
5367 TC_LOG_ERROR("sql.sql", "Spell (id: {}) have SPELL_EFFECT_QUEST_COMPLETE for quest {}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id);
5368
5369 // this will prevent quest completing without objective
5370 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
5371 }
5372 }
5373 }
5374
5375 TC_LOG_INFO("server.loading", ">> Loaded {} quests definitions in {} ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
5376}
5377
5379{
5380 TC_LOG_INFO("server.loading", "Loading GO Start Quest Data...");
5382 TC_LOG_INFO("server.loading", "Loading GO End Quest Data...");
5384 TC_LOG_INFO("server.loading", "Loading Creature Start Quest Data...");
5386 TC_LOG_INFO("server.loading", "Loading Creature End Quest Data...");
5388}
5389
5391{
5392 uint32 oldMSTime = getMSTime();
5393
5394 _questLocaleStore.clear(); // need for reload case
5395
5396 // 0 1
5397 QueryResult result = WorldDatabase.Query("SELECT ID, locale, "
5398 // 2 3 4 5 6 7 8 9 10
5399 "LogTitle, LogDescription, QuestDescription, AreaDescription, QuestCompletionLog, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4"
5400 " FROM quest_template_locale");
5401 if (!result)
5402 return;
5403
5404 do
5405 {
5406 Field* fields = result->Fetch();
5407
5408 uint32 id = fields[0].GetUInt32();
5409 std::string localeName = fields[1].GetString();
5410
5411 LocaleConstant locale = GetLocaleByName(localeName);
5412 if (locale == LOCALE_enUS)
5413 continue;
5414
5415 QuestLocale& data = _questLocaleStore[id];
5416 AddLocaleString(fields[2].GetString(), locale, data.LogTitle);
5417 AddLocaleString(fields[3].GetString(), locale, data.LogDescription);
5418 AddLocaleString(fields[4].GetString(), locale, data.QuestDescription);
5419 AddLocaleString(fields[5].GetString(), locale, data.AreaDescription);
5420 AddLocaleString(fields[6].GetString(), locale, data.QuestCompletionLog);
5421
5422 for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
5423 AddLocaleString(fields[i + 7].GetString(), locale, data.ObjectiveText[i]);
5424 } while (result->NextRow());
5425
5426 TC_LOG_INFO("server.loading", ">> Loaded {} Quest locale strings in {} ms", uint32(_questLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
5427}
5428
5430{
5431 uint32 oldMSTime = getMSTime();
5432
5433 ScriptMapMap* scripts = GetScriptsMapByType(type);
5434 if (!scripts)
5435 return;
5436
5437 std::string tableName = GetScriptsTableNameByType(type);
5438 if (tableName.empty())
5439 return;
5440
5441 if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
5442 return;
5443
5444 TC_LOG_INFO("server.loading", "Loading {}...", tableName);
5445
5446 scripts->clear(); // need for reload support
5447
5448 // 0 1 2 3 4 5 6 7 8 9
5449 QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o FROM {}", tableName);
5450
5451 if (!result)
5452 {
5453 TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `{}` is empty!", tableName);
5454 return;
5455 }
5456
5457 uint32 count = 0;
5458
5459 do
5460 {
5461 Field* fields = result->Fetch();
5462 ScriptInfo tmp;
5463 tmp.type = type;
5464 tmp.id = fields[0].GetUInt32();
5465 tmp.delay = fields[1].GetUInt32();
5466 tmp.command = ScriptCommands(fields[2].GetUInt32());
5467 tmp.Raw.nData[0] = fields[3].GetUInt32();
5468 tmp.Raw.nData[1] = fields[4].GetUInt32();
5469 tmp.Raw.nData[2] = fields[5].GetInt32();
5470 tmp.Raw.fData[0] = fields[6].GetFloat();
5471 tmp.Raw.fData[1] = fields[7].GetFloat();
5472 tmp.Raw.fData[2] = fields[8].GetFloat();
5473 tmp.Raw.fData[3] = fields[9].GetFloat();
5474
5475 // generic command args check
5476 switch (tmp.command)
5477 {
5479 {
5481 {
5482 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk type (datalong = {}) in SCRIPT_COMMAND_TALK for script id {}",
5483 tableName, tmp.Talk.ChatType, tmp.id);
5484 continue;
5485 }
5486 if (!sObjectMgr->GetBroadcastText(uint32(tmp.Talk.TextID)))
5487 {
5488 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk text id (dataint = {}) in SCRIPT_COMMAND_TALK for script id {}",
5489 tableName, tmp.Talk.TextID, tmp.id);
5490 continue;
5491 }
5492
5493 break;
5494 }
5495
5497 {
5498 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
5499 {
5500 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid emote id (datalong = {}) in SCRIPT_COMMAND_EMOTE for script id {}",
5501 tableName, tmp.Emote.EmoteID, tmp.id);
5502 continue;
5503 }
5504 break;
5505 }
5506
5508 {
5509 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
5510 {
5511 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid map (Id: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5512 tableName, tmp.TeleportTo.MapID, tmp.id);
5513 continue;
5514 }
5515
5517 {
5518 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5519 tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
5520 continue;
5521 }
5522 break;
5523 }
5524
5526 {
5527 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
5528 if (!quest)
5529 {
5530 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}",
5531 tableName, tmp.QuestExplored.QuestID, tmp.id);
5532 continue;
5533 }
5534
5536 {
5537 TC_LOG_ERROR("sql.sql", "Table `{}` has quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",
5538 tableName, tmp.QuestExplored.QuestID, tmp.id);
5539
5540 // this will prevent quest completing without objective
5541 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
5542
5543 // continue; - quest objective requirement set and command can be allowed
5544 }
5545
5547 {
5548 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 {}",
5549 tableName, tmp.QuestExplored.Distance, tmp.id);
5550 continue;
5551 }
5552
5554 {
5555 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",
5557 continue;
5558 }
5559
5561 {
5562 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",
5563 tableName, tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
5564 continue;
5565 }
5566
5567 break;
5568 }
5569
5571 {
5573 {
5574 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_KILL_CREDIT for script id {}",
5575 tableName, tmp.KillCredit.CreatureEntry, tmp.id);
5576 continue;
5577 }
5578 break;
5579 }
5580
5582 {
5584 if (!data)
5585 {
5586 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5587 tableName, tmp.RespawnGameobject.GOGuid, tmp.id);
5588 continue;
5589 }
5590
5591 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5592 if (!info)
5593 {
5594 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5595 tableName, tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
5596 continue;
5597 }
5598
5599 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
5601 info->type == GAMEOBJECT_TYPE_DOOR ||
5602 info->type == GAMEOBJECT_TYPE_BUTTON ||
5603 info->type == GAMEOBJECT_TYPE_TRAP)
5604 {
5605 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5606 tableName, info->entry, tmp.id);
5607 continue;
5608 }
5609 break;
5610 }
5611
5613 {
5615 {
5616 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5618 continue;
5619 }
5620
5622 {
5623 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5624 tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id);
5625 continue;
5626 }
5627 break;
5628 }
5629
5632 {
5634 if (!data)
5635 {
5636 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in {} for script id {}",
5637 tableName, tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command), tmp.id);
5638 continue;
5639 }
5640
5641 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5642 if (!info)
5643 {
5644 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in {} for script id {}",
5645 tableName, tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command), tmp.id);
5646 continue;
5647 }
5648
5649 if (info->type != GAMEOBJECT_TYPE_DOOR)
5650 {
5651 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command {} for script id {}",
5652 tableName, info->entry, GetScriptCommandName(tmp.command), tmp.id);
5653 continue;
5654 }
5655
5656 break;
5657 }
5658
5660 {
5661 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID))
5662 {
5663 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5664 tableName, tmp.RemoveAura.SpellID, tmp.id);
5665 continue;
5666 }
5667 if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
5668 {
5669 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in datalong2 ({}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5670 tableName, tmp.RemoveAura.Flags, tmp.id);
5671 continue;
5672 }
5673 break;
5674 }
5675
5677 {
5678 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID))
5679 {
5680 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5681 tableName, tmp.CastSpell.SpellID, tmp.id);
5682 continue;
5683 }
5684 if (tmp.CastSpell.Flags > 4) // targeting type
5685 {
5686 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown target in datalong2 ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5687 tableName, tmp.CastSpell.Flags, tmp.id);
5688 continue;
5689 }
5690 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
5691 {
5692 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5693 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5694 continue;
5695 }
5696 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
5697 {
5698 TC_LOG_ERROR("sql.sql", "Table `{}` using invalid creature entry in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5699 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5700 continue;
5701 }
5702 break;
5703 }
5704
5706 {
5708 {
5709 TC_LOG_ERROR("sql.sql", "Table `{}` has nonexistent item (entry: {}) in SCRIPT_COMMAND_CREATE_ITEM for script id {}",
5710 tableName, tmp.CreateItem.ItemEntry, tmp.id);
5711 continue;
5712 }
5713 if (!tmp.CreateItem.Amount)
5714 {
5715 TC_LOG_ERROR("sql.sql", "Table `{}` SCRIPT_COMMAND_CREATE_ITEM but amount is {} for script id {}",
5716 tableName, tmp.CreateItem.Amount, tmp.id);
5717 continue;
5718 }
5719 break;
5720 }
5721 default:
5722 break;
5723 }
5724
5725 if (scripts->find(tmp.id) == scripts->end())
5726 {
5727 ScriptMap emptyMap;
5728 (*scripts)[tmp.id] = emptyMap;
5729 }
5730 (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
5731
5732 ++count;
5733 }
5734 while (result->NextRow());
5735
5736 TC_LOG_INFO("server.loading", ">> Loaded {} script definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5737}
5738
5740{
5742
5743 std::set<uint32> evt_scripts;
5744 // Load all possible script entries from gameobjects
5745 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
5746 if (uint32 eventId = gameObjectTemplatePair.second.GetEventScriptId())
5747 evt_scripts.insert(eventId);
5748
5749 // Load all possible script entries from spells
5750 for (uint32 i = 1; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
5751 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(i))
5752 for (SpellEffectInfo const& spellEffectInfo : spell->GetEffects())
5753 if (spellEffectInfo.IsEffect(SPELL_EFFECT_SEND_EVENT))
5754 if (spellEffectInfo.MiscValue)
5755 evt_scripts.insert(spellEffectInfo.MiscValue);
5756
5757 for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
5758 {
5759 for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
5760 {
5761 TaxiPathNodeEntry const* node = sTaxiPathNodesByPath[path_idx][node_idx];
5762
5763 if (node->ArrivalEventID)
5764 evt_scripts.insert(node->ArrivalEventID);
5765
5766 if (node->DepartureEventID)
5767 evt_scripts.insert(node->DepartureEventID);
5768 }
5769 }
5770
5771 // Then check if all scripts are in above list of possible script entries
5772 for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
5773 {
5774 std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
5775 if (itr2 == evt_scripts.end())
5776 TC_LOG_ERROR("sql.sql", "Table `event_scripts` has script (Id: {}) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect {}",
5777 itr->first, SPELL_EFFECT_SEND_EVENT);
5778 }
5779}
5780
5781//Load WP Scripts
5783{
5785
5786 std::set<uint32> actionSet;
5787
5788 for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
5789 actionSet.insert(itr->first);
5790
5792 PreparedQueryResult result = WorldDatabase.Query(stmt);
5793
5794 if (result)
5795 {
5796 do
5797 {
5798 Field* fields = result->Fetch();
5799 uint32 action = fields[0].GetUInt32();
5800
5801 actionSet.erase(action);
5802 }
5803 while (result->NextRow());
5804 }
5805
5806 for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
5807 TC_LOG_ERROR("sql.sql", "There is no waypoint which links to the waypoint script {}", *itr);
5808}
5809
5811{
5812 uint32 oldMSTime = getMSTime();
5813
5814 _spellScriptsStore.clear(); // need for reload case
5815
5816 QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
5817
5818 if (!result)
5819 {
5820 TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
5821 return;
5822 }
5823
5824 uint32 count = 0;
5825
5826 do
5827 {
5828
5829 Field* fields = result->Fetch();
5830
5831 int32 spellId = fields[0].GetInt32();
5832 std::string const scriptName = fields[1].GetString();
5833
5834 bool allRanks = false;
5835 if (spellId < 0)
5836 {
5837 allRanks = true;
5838 spellId = -spellId;
5839 }
5840
5841 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
5842 if (!spellInfo)
5843 {
5844 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) does not exist.", scriptName, fields[0].GetInt32());
5845 continue;
5846 }
5847
5848 if (allRanks)
5849 {
5850 if (!spellInfo->IsRanked())
5851 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) has no ranks of spell.", scriptName, fields[0].GetInt32());
5852
5853 if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
5854 {
5855 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is not first rank of spell.", scriptName, fields[0].GetInt32());
5856 continue;
5857 }
5858
5859 while (spellInfo)
5860 {
5861 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
5862 spellInfo = spellInfo->GetNextRankSpell();
5863 }
5864 }
5865 else
5866 {
5867 if (spellInfo->IsRanked())
5868 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId);
5869
5870 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
5871 }
5872
5873 ++count;
5874 }
5875 while (result->NextRow());
5876
5877 TC_LOG_INFO("server.loading", ">> Loaded {} spell script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5878}
5879
5881{
5882 uint32 oldMSTime = getMSTime();
5883
5884 if (_spellScriptsStore.empty())
5885 {
5886 TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
5887 return;
5888 }
5889
5890 uint32 count = 0;
5891
5892 for (auto spell : _spellScriptsStore)
5893 {
5894 SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(spell.first);
5895
5896 auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
5897
5898 for (auto itr = bounds.first; itr != bounds.second; ++itr)
5899 {
5900 if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
5901 {
5902 ++count;
5903
5904 std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
5905 std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
5906
5907 if (!spellScript && !auraScript)
5908 {
5909 TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `{}` do not return objects - script skipped", GetScriptName(itr->second.first));
5910
5911 itr->second.second = false;
5912 continue;
5913 }
5914
5915 if (spellScript)
5916 {
5917 spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
5918 spellScript->_Register();
5919
5920 if (!spellScript->_Validate(spellEntry))
5921 {
5922 itr->second.second = false;
5923 continue;
5924 }
5925 }
5926
5927 if (auraScript)
5928 {
5929 auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
5930 auraScript->_Register();
5931
5932 if (!auraScript->_Validate(spellEntry))
5933 {
5934 itr->second.second = false;
5935 continue;
5936 }
5937 }
5938
5939 // Enable the script when all checks passed
5940 itr->second.second = true;
5941 }
5942 else
5943 itr->second.second = false;
5944 }
5945 }
5946
5947 TC_LOG_INFO("server.loading", ">> Validated {} scripts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5948}
5949
5951{
5952 uint32 oldMSTime = getMSTime();
5953
5954 // 0 1 2
5955 QueryResult result = WorldDatabase.Query("SELECT ID, `Text`, NextPageID FROM page_text");
5956
5957 if (!result)
5958 {
5959 TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
5960 return;
5961 }
5962
5963 uint32 count = 0;
5964 do
5965 {
5966 Field* fields = result->Fetch();
5967
5968 PageText& pageText = _pageTextStore[fields[0].GetUInt32()];
5969
5970 pageText.Text = fields[1].GetString();
5971 pageText.NextPageID = fields[2].GetUInt32();
5972
5973 ++count;
5974 }
5975 while (result->NextRow());
5976
5977 for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
5978 {
5979 if (itr->second.NextPageID)
5980 {
5981 PageTextContainer::const_iterator itr2 = _pageTextStore.find(itr->second.NextPageID);
5982 if (itr2 == _pageTextStore.end())
5983 TC_LOG_ERROR("sql.sql", "Page text (ID: {}) has non-existing `NextPageID` ({})", itr->first, itr->second.NextPageID);
5984
5985 }
5986 }
5987
5988 TC_LOG_INFO("server.loading", ">> Loaded {} page texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5989}
5990
5992{
5993 PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
5994 if (itr != _pageTextStore.end())
5995 return &(itr->second);
5996
5997 return nullptr;
5998}
5999
6001{
6002 uint32 oldMSTime = getMSTime();
6003
6004 _pageTextLocaleStore.clear(); // need for reload case
6005
6006 // 0 1 2
6007 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text` FROM page_text_locale");
6008
6009 if (!result)
6010 return;
6011
6012 do
6013 {
6014 Field* fields = result->Fetch();
6015
6016 uint32 id = fields[0].GetUInt32();
6017 std::string localeName = fields[1].GetString();
6018
6019 LocaleConstant locale = GetLocaleByName(localeName);
6020 if (locale == LOCALE_enUS)
6021 continue;
6022
6024 AddLocaleString(fields[2].GetString(), locale, data.Text);
6025 } while (result->NextRow());
6026
6027 TC_LOG_INFO("server.loading", ">> Loaded {} PageText locale strings in {} ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6028}
6029
6031{
6032 uint32 oldMSTime = getMSTime();
6033
6034 // 0 1 2 4
6035 QueryResult result = WorldDatabase.Query("SELECT map, parent, script, allowMount FROM instance_template");
6036
6037 if (!result)
6038 {
6039 TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
6040 return;
6041 }
6042
6043 uint32 count = 0;
6044 do
6045 {
6046 Field* fields = result->Fetch();
6047
6048 uint16 mapID = fields[0].GetUInt16();
6049
6050 if (!MapManager::IsValidMAP(mapID, true))
6051 {
6052 TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid {} for template!", mapID);
6053 continue;
6054 }
6055
6056 InstanceTemplate instanceTemplate;
6057
6058 instanceTemplate.AllowMount = fields[3].GetBool();
6059 instanceTemplate.Parent = uint32(fields[1].GetUInt16());
6060 instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].GetString());
6061
6062 _instanceTemplateStore[mapID] = instanceTemplate;
6063
6064 ++count;
6065 }
6066 while (result->NextRow());
6067
6068 TC_LOG_INFO("server.loading", ">> Loaded {} instance templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6069}
6070
6072{
6073 InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
6074 if (itr != _instanceTemplateStore.end())
6075 return &(itr->second);
6076
6077 return nullptr;
6078}
6079
6081{
6082 uint32 oldMSTime = getMSTime();
6083
6084 // 0 1 2 3
6085 QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
6086 if (!result)
6087 {
6088 TC_LOG_INFO("server.loading", ">> Loaded 0 instance encounters, table is empty!");
6089 return;
6090 }
6091
6092 uint32 count = 0;
6093 std::map<uint32, DungeonEncounterEntry const*> dungeonLastBosses;
6094 do
6095 {
6096 Field* fields = result->Fetch();
6097 uint32 entry = fields[0].GetUInt32();
6098 uint8 creditType = fields[1].GetUInt8();
6099 uint32 creditEntry = fields[2].GetUInt32();
6100 uint32 lastEncounterDungeon = fields[3].GetUInt16();
6101 DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
6102 if (!dungeonEncounter)
6103 {
6104 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid encounter id {}, skipped!", entry);
6105 continue;
6106 }
6107
6108 if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeonEntry(lastEncounterDungeon))
6109 {
6110 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an encounter {} ({}) marked as final for invalid dungeon id {}, skipped!", entry, dungeonEncounter->Name[0], lastEncounterDungeon);
6111 continue;
6112 }
6113
6114 auto itr = dungeonLastBosses.find(lastEncounterDungeon);
6115 if (lastEncounterDungeon)
6116 {
6117 if (itr != dungeonLastBosses.end())
6118 {
6119 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` specified encounter {} ({}) as last encounter but {} ({}) is already marked as one, skipped!", entry, dungeonEncounter->Name[0], itr->second->ID, itr->second->Name[0]);
6120 continue;
6121 }
6122
6123 dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
6124 }
6125
6126 switch (creditType)
6127 {
6129 {
6130 CreatureTemplate const* creatureInfo = GetCreatureTemplate(creditEntry);
6131 if (!creatureInfo)
6132 {
6133 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid creature (entry {}) linked to the encounter {} ({}), skipped!", creditEntry, entry, dungeonEncounter->Name[0]);
6134 continue;
6135 }
6136 const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
6137 for (uint8 diff = 1; diff < MAX_DIFFICULTY; ++diff)
6138 {
6139 if (uint32 diffEntry = creatureInfo->DifficultyEntry[diff - 1])
6140 {
6141 if (CreatureTemplate const* diffInfo = GetCreatureTemplate(diffEntry))
6143 }
6144 }
6145 break;
6146 }
6148 if (!sSpellMgr->GetSpellInfo(creditEntry))
6149 {
6150 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid spell (entry {}) linked to the encounter {} ({}), skipped!", creditEntry, entry, dungeonEncounter->Name[0]);
6151 continue;
6152 }
6153 break;
6154 default:
6155 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid credit type ({}) for encounter {} ({}), skipped!", creditType, entry, dungeonEncounter->Name[0]);
6156 continue;
6157 }
6158
6159 DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->MapID, dungeonEncounter->Difficulty)];
6160 encounters.emplace_back(std::make_unique<DungeonEncounter>(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
6161 ++count;
6162 } while (result->NextRow());
6163
6164 TC_LOG_INFO("server.loading", ">> Loaded {} instance encounters in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6165}
6166
6168{
6169 GossipTextContainer::const_iterator itr = _gossipTextStore.find(Text_ID);
6170 if (itr != _gossipTextStore.end())
6171 return &itr->second;
6172 return nullptr;
6173}
6174
6176{
6177 uint32 oldMSTime = getMSTime();
6178
6179 QueryResult result = WorldDatabase.Query("SELECT ID, "
6180 "text0_0, text0_1, BroadcastTextID0, lang0, Probability0, EmoteDelay0_0, Emote0_0, EmoteDelay0_1, Emote0_1, EmoteDelay0_2, Emote0_2, "
6181 "text1_0, text1_1, BroadcastTextID1, lang1, Probability1, EmoteDelay1_0, Emote1_0, EmoteDelay1_1, Emote1_1, EmoteDelay1_2, Emote1_2, "
6182 "text2_0, text2_1, BroadcastTextID2, lang2, Probability2, EmoteDelay2_0, Emote2_0, EmoteDelay2_1, Emote2_1, EmoteDelay2_2, Emote2_2, "
6183 "text3_0, text3_1, BroadcastTextID3, lang3, Probability3, EmoteDelay3_0, Emote3_0, EmoteDelay3_1, Emote3_1, EmoteDelay3_2, Emote3_2, "
6184 "text4_0, text4_1, BroadcastTextID4, lang4, Probability4, EmoteDelay4_0, Emote4_0, EmoteDelay4_1, Emote4_1, EmoteDelay4_2, Emote4_2, "
6185 "text5_0, text5_1, BroadcastTextID5, lang5, Probability5, EmoteDelay5_0, Emote5_0, EmoteDelay5_1, Emote5_1, EmoteDelay5_2, Emote5_2, "
6186 "text6_0, text6_1, BroadcastTextID6, lang6, Probability6, EmoteDelay6_0, Emote6_0, EmoteDelay6_1, Emote6_1, EmoteDelay6_2, Emote6_2, "
6187 "text7_0, text7_1, BroadcastTextID7, lang7, Probability7, EmoteDelay7_0, Emote7_0, EmoteDelay7_1, Emote7_1, EmoteDelay7_2, Emote7_2 "
6188 "FROM npc_text");
6189
6190 if (!result)
6191 {
6192 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6193 return;
6194 }
6195
6196 _gossipTextStore.rehash(result->GetRowCount());
6197
6198 uint32 count = 0;
6199 uint8 cic;
6200
6201 do
6202 {
6203 ++count;
6204 cic = 0;
6205
6206 Field* fields = result->Fetch();
6207
6208 uint32 id = fields[cic++].GetUInt32();
6209 if (!id)
6210 {
6211 TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
6212 continue;
6213 }
6214
6215 GossipText& gText = _gossipTextStore[id];
6216
6217 for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
6218 {
6219 GossipTextOption& gOption = gText.Options[i];
6220 gOption.Text_0 = fields[cic++].GetString();
6221 gOption.Text_1 = fields[cic++].GetString();
6222 gOption.BroadcastTextID = fields[cic++].GetUInt32();
6223 gOption.Language = fields[cic++].GetUInt8();
6224 gOption.Probability = fields[cic++].GetFloat();
6225
6226 for (uint8 j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
6227 {
6228 gOption.Emotes[j]._Delay = fields[cic++].GetUInt16();
6229 gOption.Emotes[j]._Emote = fields[cic++].GetUInt16();
6230 }
6231
6232 // check broadcast_text correctness
6233 if (gOption.BroadcastTextID)
6234 {
6235 if (BroadcastTextEntry const* bcText = sObjectMgr->GetBroadcastText(gOption.BroadcastTextID))
6236 {
6237 if (bcText->Text[DEFAULT_LOCALE] != gOption.Text_0)
6238 TC_LOG_ERROR("sql.sql", "Row {} in table `npc_text` has mismatch between text{}_0 and the corresponding Text in `broadcast_text` row {}", id, i, gOption.BroadcastTextID);
6239 if (bcText->Text1[DEFAULT_LOCALE] != gOption.Text_1)
6240 TC_LOG_ERROR("sql.sql", "Row {} in table `npc_text` has mismatch between text{}_1 and the corresponding Text1 in `broadcast_text` row {}", id, i, gOption.BroadcastTextID);
6241 }
6242 else
6243 {
6244 TC_LOG_ERROR("sql.sql", "GossipText (Id: {}) in table `npc_text` has non-existing or incompatible BroadcastTextID{} {}.", id, i, gOption.BroadcastTextID);
6245 gOption.BroadcastTextID = 0;
6246 }
6247
6248 }
6249 }
6250
6251 }
6252 while (result->NextRow());
6253
6254 TC_LOG_INFO("server.loading", ">> Loaded {} npc texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6255}
6256
6258{
6259 uint32 oldMSTime = getMSTime();
6260
6261 _npcTextLocaleStore.clear(); // need for reload case
6262
6263 QueryResult result = WorldDatabase.Query("SELECT ID, Locale, "
6264 // 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
6265 "Text0_0, Text0_1, Text1_0, Text1_1, Text2_0, Text2_1, Text3_0, Text3_1, Text4_0, Text4_1, Text5_0, Text5_1, Text6_0, Text6_1, Text7_0, Text7_1 "
6266 "FROM npc_text_locale");
6267
6268 if (!result)
6269 return;
6270
6271 do
6272 {
6273 Field* fields = result->Fetch();
6274
6275 uint32 id = fields[0].GetUInt32();
6276 std::string localeName = fields[1].GetString();
6277
6278 LocaleConstant locale = GetLocaleByName(localeName);
6279 if (locale == LOCALE_enUS)
6280 continue;
6281
6283 for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
6284 {
6285 AddLocaleString(fields[2 + i * 2].GetString(), locale, data.Text_0[i]);
6286 AddLocaleString(fields[3 + i * 2].GetString(), locale, data.Text_1[i]);
6287 }
6288 } while (result->NextRow());
6289
6290 TC_LOG_INFO("server.loading", ">> Loaded {} NpcText locale strings in {} ms", uint32(_npcTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6291}
6292
6293//not very fast function but it is called only once a day, or on starting-up
6295{
6296 uint32 oldMSTime = getMSTime();
6297
6298 time_t curTime = GameTime::GetGameTime();
6299 tm lt;
6300 localtime_r(&curTime, &lt);
6301 uint64 basetime(curTime);
6302 TC_LOG_INFO("misc", "Returning mails current time: hour: {}, minute: {}, second: {} ", lt.tm_hour, lt.tm_min, lt.tm_sec);
6303
6304 // Delete all old mails without item and without body immediately, if starting server
6305 if (!serverUp)
6306 {
6308 stmt->setUInt64(0, basetime);
6309 CharacterDatabase.Execute(stmt);
6310 }
6312 stmt->setUInt64(0, basetime);
6313 PreparedQueryResult result = CharacterDatabase.Query(stmt);
6314 if (!result)
6315 {
6316 TC_LOG_INFO("server.loading", ">> No expired mails found.");
6317 return; // any mails need to be returned or deleted
6318 }
6319
6320 std::map<uint32 /*messageId*/, MailItemInfoVec> itemsCache;
6321 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
6322 stmt->setUInt32(0, (uint32)basetime);
6323 if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
6324 {
6325 MailItemInfo item;
6326 do
6327 {
6328 Field* fields = items->Fetch();
6329 item.item_guid = fields[0].GetUInt32();
6330 item.item_template = fields[1].GetUInt32();
6331 uint32 mailId = fields[2].GetUInt32();
6332 itemsCache[mailId].push_back(item);
6333 } while (items->NextRow());
6334 }
6335
6336 uint32 deletedCount = 0;
6337 uint32 returnedCount = 0;
6338 do
6339 {
6340 Field* fields = result->Fetch();
6341 ObjectGuid::LowType receiver = fields[3].GetUInt32();
6342 if (serverUp && ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(receiver)))
6343 continue;
6344
6345 Mail* m = new Mail;
6346 m->messageID = fields[0].GetUInt32();
6347 m->messageType = fields[1].GetUInt8();
6348 m->sender = fields[2].GetUInt32();
6349 m->receiver = receiver;
6350 bool has_items = fields[4].GetBool();
6351 m->expire_time = time_t(fields[5].GetUInt32());
6352 m->deliver_time = 0;
6353 m->COD = fields[6].GetUInt32();
6354 m->checked = fields[7].GetUInt32();
6355 m->mailTemplateId = fields[8].GetInt16();
6356
6357 // Delete or return mail
6358 if (has_items)
6359 {
6360 // read items from cache
6361 m->items.swap(itemsCache[m->messageID]);
6362
6363 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
6365 {
6366 // mail open and then not returned
6367 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6368 {
6369 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
6370 stmt->setUInt32(0, itr2->item_guid);
6371 CharacterDatabase.Execute(stmt);
6372 }
6373
6374 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
6375 stmt->setUInt32(0, m->messageID);
6376 CharacterDatabase.Execute(stmt);
6377 }
6378 else
6379 {
6380 // Mail will be returned
6381 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
6382 stmt->setUInt32(0, m->receiver);
6383 stmt->setUInt32(1, m->sender);
6384 stmt->setUInt32(2, basetime + 30 * DAY);
6385 stmt->setUInt32(3, basetime);
6387 stmt->setUInt32(5, m->messageID);
6388 CharacterDatabase.Execute(stmt);
6389 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6390 {
6391 // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
6392 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
6393 stmt->setUInt32(0, m->sender);
6394 stmt->setUInt32(1, itr2->item_guid);
6395 CharacterDatabase.Execute(stmt);
6396
6397 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
6398 stmt->setUInt32(0, m->sender);
6399 stmt->setUInt32(1, itr2->item_guid);
6400 CharacterDatabase.Execute(stmt);
6401 }
6402 delete m;
6403 ++returnedCount;
6404 continue;
6405 }
6406 }
6407
6408 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
6409 stmt->setUInt32(0, m->messageID);
6410 CharacterDatabase.Execute(stmt);
6411 delete m;
6412 ++deletedCount;
6413 }
6414 while (result->NextRow());
6415
6416 TC_LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
6417}
6418
6420{
6421 uint32 oldMSTime = getMSTime();
6422
6423 _questAreaTriggerStore.clear(); // need for reload case
6424
6425 QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
6426
6427 if (!result)
6428 {
6429 TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
6430 return;
6431 }
6432
6433 uint32 count = 0;
6434
6435 do
6436 {
6437 ++count;
6438
6439 Field* fields = result->Fetch();
6440
6441 uint32 trigger_ID = fields[0].GetUInt32();
6442 uint32 quest_ID = fields[1].GetUInt32();
6443
6444 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
6445 if (!atEntry)
6446 {
6447 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", trigger_ID);
6448 continue;
6449 }
6450
6451 Quest const* quest = GetQuestTemplate(quest_ID);
6452
6453 if (!quest)
6454 {
6455 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not existing quest {}", trigger_ID, quest_ID);
6456 continue;
6457 }
6458
6460 {
6461 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not quest {}, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
6462
6463 // this will prevent quest completing without objective
6464 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
6465
6466 // continue; - quest modified to required objective and trigger can be allowed.
6467 }
6468
6469 _questAreaTriggerStore[trigger_ID] = quest_ID;
6470
6471 } while (result->NextRow());
6472
6473 TC_LOG_INFO("server.loading", ">> Loaded {} quest trigger points in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6474}
6475
6477{
6478 auto itr = _questGreetingStore.find(guid.GetTypeId());
6479 if (itr == _questGreetingStore.end())
6480 return nullptr;
6481
6482 auto questItr = itr->second.find(guid.GetEntry());
6483 if (questItr == itr->second.end())
6484 return nullptr;
6485
6486 return &questItr->second;
6487}
6488
6490{
6491 uint32 oldMSTime = getMSTime();
6492
6493 _questGreetingStore.clear(); // need for reload case
6494
6495 // 0 1 2 3 4
6496 QueryResult result = WorldDatabase.Query("SELECT ID, Type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
6497 if (!result)
6498 {
6499 TC_LOG_INFO("server.loading", ">> Loaded 0 quest greetings. DB table `quest_greeting` is empty.");
6500 return;
6501 }
6502
6503 _questGreetingStore.rehash(result->GetRowCount());
6504
6505 uint32 count = 0;
6506
6507 do
6508 {
6509 Field* fields = result->Fetch();
6510
6511 uint32 id = fields[0].GetUInt32();
6512 uint8 type = fields[1].GetUInt8();
6513 // overwrite
6514 switch (type)
6515 {
6516 case 0: // Creature
6517 type = TYPEID_UNIT;
6518 if (!sObjectMgr->GetCreatureTemplate(id))
6519 {
6520 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template (entry: {}) does not exist.", id);
6521 continue;
6522 }
6523 break;
6524 case 1: // GameObject
6525 type = TYPEID_GAMEOBJECT;
6526 if (!sObjectMgr->GetGameObjectTemplate(id))
6527 {
6528 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template (entry: {}) does not exist.", id);
6529 continue;
6530 }
6531 break;
6532 default:
6533 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: unknown type = {} for entry = {}. Skipped.", type, id);
6534 continue;
6535 }
6536
6537 uint16 greetEmoteType = fields[2].GetUInt16();
6538
6539 if (greetEmoteType > 0 && !sEmotesStore.LookupEntry(greetEmoteType))
6540 {
6541 TC_LOG_DEBUG("sql.sql", "Table `quest_greeting`: entry {} has greetEmoteType = {} but emote does not exist. Set to 0.", id, greetEmoteType);
6542 greetEmoteType = 0;
6543 }
6544
6545 uint32 greetEmoteDelay = fields[3].GetUInt32();
6546 std::string greeting = fields[4].GetString();
6547
6548 _questGreetingStore[type][id] = QuestGreeting(greetEmoteType, greetEmoteDelay, greeting);
6549
6550 ++count;
6551 }
6552 while (result->NextRow());
6553
6554 TC_LOG_INFO("server.loading", ">> Loaded {} quest_greeting in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6555}
6556
6558{
6559 uint32 oldMSTime = getMSTime();
6560
6561 _questGreetingLocaleStore.clear(); // need for reload case
6562
6563 // 0 1 2 3
6564 QueryResult result = WorldDatabase.Query("SELECT ID, Type, Locale, Greeting FROM quest_greeting_locale");
6565 if (!result)
6566 {
6567 TC_LOG_INFO("server.loading", ">> Loaded 0 quest_greeting locales. DB table `quest_greeting_locale` is empty.");
6568 return;
6569 }
6570
6571 do
6572 {
6573 Field* fields = result->Fetch();
6574
6575 uint32 id = fields[0].GetUInt32();
6576 uint8 type = fields[1].GetUInt8();
6577 // overwrite
6578 switch (type)
6579 {
6580 case 0: // Creature
6581 type = TYPEID_UNIT;
6582 break;
6583 case 1: // GameObject
6584 type = TYPEID_GAMEOBJECT;
6585 break;
6586 default:
6587 break;
6588 }
6589
6590 std::string localeName = fields[2].GetString();
6591
6592 LocaleConstant locale = GetLocaleByName(localeName);
6593 if (locale == LOCALE_enUS)
6594 continue;
6595
6597 AddLocaleString(fields[3].GetString(), locale, data.greeting);
6598 } while (result->NextRow());
6599
6600 TC_LOG_INFO("server.loading", ">> Loaded {} quest greeting locale strings in {} ms", uint32(_questGreetingLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6601}
6602
6604{
6605 uint32 oldMSTime = getMSTime();
6606
6607 _questOfferRewardLocaleStore.clear(); // need for reload case
6608 // 0 1 2
6609 QueryResult result = WorldDatabase.Query("SELECT Id, locale, RewardText FROM quest_offer_reward_locale");
6610 if (!result)
6611 return;
6612
6613 do
6614 {
6615 Field* fields = result->Fetch();
6616
6617 uint32 id = fields[0].GetUInt32();
6618 std::string localeName = fields[1].GetString();
6619
6620 LocaleConstant locale = GetLocaleByName(localeName);
6621 if (locale == LOCALE_enUS)
6622 continue;
6623
6625 AddLocaleString(fields[2].GetString(), locale, data.RewardText);
6626 } while (result->NextRow());
6627
6628 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Offer Reward locale strings in {} ms", _questOfferRewardLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
6629}
6630
6632{
6633 uint32 oldMSTime = getMSTime();
6634
6635 _questRequestItemsLocaleStore.clear(); // need for reload case
6636 // 0 1 2
6637 QueryResult result = WorldDatabase.Query("SELECT Id, locale, CompletionText FROM quest_request_items_locale");
6638 if (!result)
6639 return;
6640
6641 do
6642 {
6643 Field* fields = result->Fetch();
6644
6645 uint32 id = fields[0].GetUInt32();
6646 std::string localeName = fields[1].GetString();
6647
6648 LocaleConstant locale = GetLocaleByName(localeName);
6649 if (locale == LOCALE_enUS)
6650 continue;
6651
6653 AddLocaleString(fields[2].GetString(), locale, data.CompletionText);
6654 } while (result->NextRow());
6655
6656 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Request Items locale strings in {} ms", _questRequestItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
6657}
6658
6660{
6661 uint32 oldMSTime = getMSTime();
6662
6663 _tavernAreaTriggerStore.clear(); // need for reload case
6664
6665 QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
6666
6667 if (!result)
6668 {
6669 TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
6670 return;
6671 }
6672
6673 uint32 count = 0;
6674
6675 do
6676 {
6677 ++count;
6678
6679 Field* fields = result->Fetch();
6680
6681 uint32 Trigger_ID = fields[0].GetUInt32();
6682
6683 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
6684 if (!atEntry)
6685 {
6686 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
6687 continue;
6688 }
6689
6690 _tavernAreaTriggerStore.insert(Trigger_ID);
6691 } while (result->NextRow());
6692
6693 TC_LOG_INFO("server.loading", ">> Loaded {} tavern triggers in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6694}
6695
6697{
6698 uint32 oldMSTime = getMSTime();
6699
6700 _areaTriggerScriptStore.clear(); // need for reload case
6701
6702 QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
6703 if (!result)
6704 {
6705 TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
6706 return;
6707 }
6708
6709 do
6710 {
6711 Field* fields = result->Fetch();
6712
6713 uint32 triggerId = fields[0].GetUInt32();
6714 std::string const scriptName = fields[1].GetString();
6715
6716 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
6717 if (!atEntry)
6718 {
6719 TC_LOG_ERROR("sql.sql", "AreaTrigger (ID: {}) does not exist in `AreaTrigger.dbc`.", triggerId);
6720 continue;
6721 }
6722 _areaTriggerScriptStore[triggerId] = GetScriptId(scriptName);
6723 }
6724 while (result->NextRow());
6725
6726 TC_LOG_INFO("server.loading", ">> Loaded {} areatrigger scripts in {} ms", _areaTriggerScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
6727}
6728
6729uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
6730{
6731 bool found = false;
6732 float dist = 10000;
6733 uint32 id = 0;
6734
6735 for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
6736 {
6737 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
6738
6739 if (!node || node->ContinentID != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981)) // dk flight
6740 continue;
6741
6742 uint32 field = uint32((node->ID - 1) / (sizeof(TaxiMask::value_type) * 8));
6743 TaxiMask::value_type submask = TaxiMask::value_type(1 << ((node->ID - 1) % (sizeof(TaxiMask::value_type) * 8)));
6744
6745 // skip not taxi network nodes
6746 if ((sTaxiNodesMask[field] & submask) == 0)
6747 continue;
6748
6749 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);
6750 if (found)
6751 {
6752 if (dist2 < dist)
6753 {
6754 dist = dist2;
6755 id = i;
6756 }
6757 }
6758 else
6759 {
6760 found = true;
6761 dist = dist2;
6762 id = i;
6763 }
6764 }
6765
6766 return id;
6767}
6768
6769void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
6770{
6771 TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
6772 if (src_i == sTaxiPathSetBySource.end())
6773 {
6774 path = 0;
6775 cost = 0;
6776 return;
6777 }
6778
6779 TaxiPathSetForSource& pathSet = src_i->second;
6780
6781 TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
6782 if (dest_i == pathSet.end())
6783 {
6784 path = 0;
6785 cost = 0;
6786 return;
6787 }
6788
6789 cost = dest_i->second.price;
6790 path = dest_i->second.ID;
6791}
6792
6793uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
6794{
6795 uint32 mount_id = 0;
6796
6797 // select mount creature id
6798 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
6799 if (node)
6800 {
6801 uint32 mount_entry = 0;
6802 if (team == ALLIANCE)
6803 mount_entry = node->MountCreatureID[1];
6804 else
6805 mount_entry = node->MountCreatureID[0];
6806
6807 // Fix for Alliance not being able to use Acherus taxi
6808 // only one mount type for both sides
6809 if (mount_entry == 0 && allowed_alt_team)
6810 {
6811 // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
6812 mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
6813 }
6814
6815 CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry);
6816 if (mount_info)
6817 {
6818 mount_id = mount_info->GetRandomValidModelId();
6819 if (!mount_id)
6820 {
6821 TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry {}! Can't load it!", mount_entry);
6822 return 0;
6823 }
6824 }
6825 }
6826
6827 // minfo is not actually used but the mount_id was updated
6829
6830 return mount_id;
6831}
6832
6834{
6835 auto itr = _questTemplates.find(quest_id);
6836 return itr != _questTemplates.end() ? itr->second.get() : nullptr;
6837}
6838
6840{
6841 uint32 oldMSTime = getMSTime();
6842
6843 GraveyardStore.clear(); // need for reload case
6844
6845 // 0 1 2
6846 QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone");
6847
6848 if (!result)
6849 {
6850 TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
6851 return;
6852 }
6853
6854 uint32 count = 0;
6855
6856 do
6857 {
6858 ++count;
6859
6860 Field* fields = result->Fetch();
6861
6862 uint32 safeLocId = fields[0].GetUInt32();
6863 uint32 zoneId = fields[1].GetUInt32();
6864 uint32 team = fields[2].GetUInt16();
6865
6866 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
6867 if (!entry)
6868 {
6869 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: {}), skipped.", safeLocId);
6870 continue;
6871 }
6872
6873 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
6874 if (!areaEntry)
6875 {
6876 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: {}), skipped.", zoneId);
6877 continue;
6878 }
6879
6880 if (areaEntry->ParentAreaID != 0)
6881 {
6882 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for SubZone (ID: {}) instead of zone, skipped.", zoneId);
6883 continue;
6884 }
6885
6886 if (team != 0 && team != HORDE && team != ALLIANCE)
6887 {
6888 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction ({}), skipped.", team);
6889 continue;
6890 }
6891
6892 if (!AddGraveyardLink(safeLocId, zoneId, team, false))
6893 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: {}) and Zone (ID: {}), skipped.", safeLocId, zoneId);
6894 } while (result->NextRow());
6895
6896 TC_LOG_INFO("server.loading", ">> Loaded {} graveyard-zone links in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6897}
6898
6900{
6901 enum DefaultGraveyard
6902 {
6903 HORDE_GRAVEYARD = 10, // Crossroads
6904 ALLIANCE_GRAVEYARD = 4 // Westfall
6905 };
6906
6907 if (team == HORDE)
6908 return sWorldSafeLocsStore.LookupEntry(HORDE_GRAVEYARD);
6909 else if (team == ALLIANCE)
6910 return sWorldSafeLocsStore.LookupEntry(ALLIANCE_GRAVEYARD);
6911 else return nullptr;
6912}
6913
6914WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(float x, float y, float z, uint32 MapId, uint32 team, WorldObject* conditionObject) const
6915{
6916 // search for zone associated closest graveyard
6917 uint32 zoneId = sMapMgr->GetZoneId(PHASEMASK_NORMAL, MapId, x, y, z);
6918
6919 if (!zoneId)
6920 {
6921 if (z > -500)
6922 {
6923 TC_LOG_ERROR("misc", "ZoneId not found for map {} coords ({}, {}, {}), object name: {} {}", MapId, x, y, z,
6924 conditionObject ? std::string_view(conditionObject->GetName()) : "", Object::GetGUID(conditionObject));
6925 return GetDefaultGraveyard(team);
6926 }
6927 }
6928
6929 // Simulate std. algorithm:
6930 // found some graveyard associated to (ghost_zone, ghost_map)
6931 //
6932 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
6933 // then check faction
6934 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
6935 // then check faction
6936 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6937 MapEntry const* map = sMapStore.LookupEntry(MapId);
6938
6939 // not need to check validity of map object; MapId _MUST_ be valid here
6940 if (range.first == range.second && !map->IsBattlegroundOrArena())
6941 {
6942 if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones
6943 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
6944 return GetDefaultGraveyard(team);
6945 }
6946
6947 // at corpse map
6948 bool foundNear = false;
6949 float distNear = 10000;
6950 WorldSafeLocsEntry const* entryNear = nullptr;
6951
6952 // at entrance map for corpse map
6953 bool foundEntr = false;
6954 float distEntr = 10000;
6955 WorldSafeLocsEntry const* entryEntr = nullptr;
6956
6957 // some where other
6958 WorldSafeLocsEntry const* entryFar = nullptr;
6959
6960 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
6961
6962 for (; range.first != range.second; ++range.first)
6963 {
6964 GraveyardData const& data = range.first->second;
6965
6966 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
6967 if (!entry)
6968 {
6969 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID {}), skipped.", data.safeLocId);
6970 continue;
6971 }
6972
6973 // skip enemy faction graveyard
6974 // team == 0 case can be at call from .neargrave
6975 if (data.team != 0 && team != 0 && data.team != team)
6976 continue;
6977
6978 // find now nearest graveyard at other map
6979 if (MapId != entry->Continent)
6980 {
6981 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
6982 if (!mapEntry
6983 || mapEntry->CorpseMapID < 0
6984 || uint32(mapEntry->CorpseMapID) != entry->Continent
6985 || (mapEntry->Corpse.X == 0 && mapEntry->Corpse.Y == 0))
6986 {
6987 // not have any corrdinates for check distance anyway
6988 entryFar = entry;
6989 continue;
6990 }
6991
6992 // at entrance map calculate distance (2D);
6993 float dist2 = (entry->Loc.X - mapEntry->Corpse.X)*(entry->Loc.X - mapEntry->Corpse.X)
6994 +(entry->Loc.Y - mapEntry->Corpse.Y)*(entry->Loc.Y - mapEntry->Corpse.Y);
6995 if (foundEntr)
6996 {
6997 if (dist2 < distEntr)
6998 {
6999 distEntr = dist2;
7000 entryEntr = entry;
7001 }
7002 }
7003 else
7004 {
7005 foundEntr = true;
7006 distEntr = dist2;
7007 entryEntr = entry;
7008 }
7009 }
7010 // find now nearest graveyard at same map
7011 else
7012 {
7013 float dist2 = (entry->Loc.X - x)*(entry->Loc.X - x)+(entry->Loc.Y - y)*(entry->Loc.Y - y)+(entry->Loc.Z - z)*(entry->Loc.Z - z);
7014 if (foundNear)
7015 {
7016 if (dist2 < distNear)
7017 {
7018 distNear = dist2;
7019 entryNear = entry;
7020 }
7021 }
7022 else
7023 {
7024 foundNear = true;
7025 distNear = dist2;
7026 entryNear = entry;
7027 }
7028 }
7029 }
7030
7031 if (entryNear)
7032 return entryNear;
7033
7034 if (entryEntr)
7035 return entryEntr;
7036
7037 return entryFar;
7038}
7039
7041{
7042 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
7043 for (; range.first != range.second; ++range.first)
7044 {
7045 GraveyardData const& data = range.first->second;
7046 if (data.safeLocId == id)
7047 return &data;
7048 }
7049 return nullptr;
7050}
7051
7053{
7054 AreaTriggerContainer::const_iterator itr = _areaTriggerStore.find(trigger);
7055 if (itr != _areaTriggerStore.end())
7056 return &itr->second;
7057 return nullptr;
7058}
7059
7061{
7062 AccessRequirementContainer::const_iterator itr = _accessRequirementStore.find(MAKE_PAIR32(mapid, difficulty));
7063 if (itr != _accessRequirementStore.end())
7064 return itr->second.get();
7065 return nullptr;
7066}
7067
7068bool ObjectMgr::AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
7069{
7070 if (FindGraveyardData(id, zoneId))
7071 return false;
7072
7073 // add link to loaded data
7074 GraveyardData data;
7075 data.safeLocId = id;
7076 data.team = team;
7077
7078 GraveyardStore.insert(GraveyardContainer::value_type(zoneId, data));
7079
7080 // add link to DB
7081 if (persist)
7082 {
7084
7085 stmt->setUInt32(0, id);
7086 stmt->setUInt32(1, zoneId);
7087 stmt->setUInt16(2, uint16(team));
7088
7089 WorldDatabase.Execute(stmt);
7090 }
7091
7092 return true;
7093}
7094
7095void ObjectMgr::RemoveGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= false*/)
7096{
7097 GraveyardMapBoundsNonConst range = GraveyardStore.equal_range(zoneId);
7098 if (range.first == range.second)
7099 {
7100 //TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
7101 return;
7102 }
7103
7104 bool found = false;
7105
7106 for (; range.first != range.second; ++range.first)
7107 {
7108 GraveyardData & data = range.first->second;
7109
7110 // skip not matching safezone id
7111 if (data.safeLocId != id)
7112 continue;
7113
7114 // skip enemy faction graveyard at same map (normal area, city, or battleground)
7115 // team == 0 case can be at call from .neargrave
7116 if (data.team != 0 && team != 0 && data.team != team)
7117 continue;
7118
7119 found = true;
7120 break;
7121 }
7122
7123 // no match, return
7124 if (!found)
7125 return;
7126
7127 // remove from links
7128 GraveyardStore.erase(range.first);
7129
7130 // remove link from DB
7131 if (persist)
7132 {
7134
7135 stmt->setUInt32(0, id);
7136 stmt->setUInt32(1, zoneId);
7137 stmt->setUInt16(2, uint16(team));
7138
7139 WorldDatabase.Execute(stmt);
7140 }
7141}
7142
7144{
7145 uint32 oldMSTime = getMSTime();
7146
7147 _areaTriggerStore.clear(); // need for reload case
7148
7149 // 0 1 2 3 4 5
7150 QueryResult result = WorldDatabase.Query("SELECT ID, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
7151 if (!result)
7152 {
7153 TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
7154 return;
7155 }
7156
7157 uint32 count = 0;
7158
7159 do
7160 {
7161 Field* fields = result->Fetch();
7162
7163 ++count;
7164
7165 uint32 Trigger_ID = fields[0].GetUInt32();
7166
7167 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
7168 if (!atEntry)
7169 {
7170 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
7171 continue;
7172 }
7173
7175
7176 at.target_mapId = fields[1].GetUInt16();
7177 at.target_X = fields[2].GetFloat();
7178 at.target_Y = fields[3].GetFloat();
7179 at.target_Z = fields[4].GetFloat();
7180 at.target_Orientation = fields[5].GetFloat();
7181
7182 MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
7183 if (!mapEntry)
7184 {
7185 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) target map (ID: {}) does not exist in `Map.dbc`.", Trigger_ID, at.target_mapId);
7186 continue;
7187 }
7188
7189 if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
7190 {
7191 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) target coordinates not provided.", Trigger_ID);
7192 continue;
7193 }
7194
7195 _areaTriggerStore[Trigger_ID] = at;
7196
7197 } while (result->NextRow());
7198
7199 TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7200}
7201
7203{
7204 uint32 oldMSTime = getMSTime();
7205
7206 _accessRequirementStore.clear(); // need for reload case
7207
7208 // 0 1 2 3 4 5 6 7 8 9 10
7209 QueryResult result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item_level, item, item2, quest_done_A, quest_done_H, completed_achievement, quest_failed_text FROM access_requirement");
7210
7211 if (!result)
7212 {
7213 TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
7214 return;
7215 }
7216
7217 uint32 count = 0;
7218
7219 do
7220 {
7221 Field* fields = result->Fetch();
7222
7223 ++count;
7224
7225 uint32 mapid = fields[0].GetUInt32();
7226 uint8 difficulty = fields[1].GetUInt8();
7227 uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);
7228
7229 auto& ar = _accessRequirementStore[requirement_ID];
7230 ar = std::make_unique<AccessRequirement>();
7231
7232 ar->levelMin = fields[2].GetUInt8();
7233 ar->levelMax = fields[3].GetUInt8();
7234 ar->item_level = fields[4].GetUInt16();
7235 ar->item = fields[5].GetUInt32();
7236 ar->item2 = fields[6].GetUInt32();
7237 ar->quest_A = fields[7].GetUInt32();
7238 ar->quest_H = fields[8].GetUInt32();
7239 ar->achievement = fields[9].GetUInt32();
7240 ar->questFailedText = fields[10].GetString();
7241
7242 if (ar->item)
7243 {
7244 ItemTemplate const* pProto = GetItemTemplate(ar->item);
7245 if (!pProto)
7246 {
7247 TC_LOG_ERROR("misc", "Key item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item, mapid, difficulty);
7248 ar->item = 0;
7249 }
7250 }
7251
7252 if (ar->item2)
7253 {
7254 ItemTemplate const* pProto = GetItemTemplate(ar->item2);
7255 if (!pProto)
7256 {
7257 TC_LOG_ERROR("misc", "Second item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item2, mapid, difficulty);
7258 ar->item2 = 0;
7259 }
7260 }
7261
7262 if (ar->quest_A)
7263 {
7264 if (!GetQuestTemplate(ar->quest_A))
7265 {
7266 TC_LOG_ERROR("sql.sql", "Required Alliance Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_A, mapid, difficulty);
7267 ar->quest_A = 0;
7268 }
7269 }
7270
7271 if (ar->quest_H)
7272 {
7273 if (!GetQuestTemplate(ar->quest_H))
7274 {
7275 TC_LOG_ERROR("sql.sql", "Required Horde Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_H, mapid, difficulty);
7276 ar->quest_H = 0;
7277 }
7278 }
7279
7280 if (ar->achievement)
7281 {
7282 if (!sAchievementStore.LookupEntry(ar->achievement))
7283 {
7284 TC_LOG_ERROR("sql.sql", "Required Achievement {} not exist for map {} difficulty {}, remove quest done requirement.", ar->achievement, mapid, difficulty);
7285 ar->achievement = 0;
7286 }
7287 }
7288 } while (result->NextRow());
7289
7290 TC_LOG_INFO("server.loading", ">> Loaded {} access requirement definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7291}
7292
7293/*
7294 * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
7295 */
7297{
7298 bool useParentDbValue = false;
7299 uint32 parentId = 0;
7300 MapEntry const* mapEntry = sMapStore.LookupEntry(Map);
7301 if (!mapEntry || mapEntry->CorpseMapID < 0)
7302 return nullptr;
7303
7304 if (mapEntry->IsDungeon())
7305 {
7306 InstanceTemplate const* iTemplate = sObjectMgr->GetInstanceTemplate(Map);
7307
7308 if (!iTemplate)
7309 return nullptr;
7310
7311 parentId = iTemplate->Parent;
7312 useParentDbValue = true;
7313 }
7314
7315 uint32 entrance_map = uint32(mapEntry->CorpseMapID);
7316 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7317 if ((!useParentDbValue && itr->second.target_mapId == entrance_map) || (useParentDbValue && itr->second.target_mapId == parentId))
7318 {
7319 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7320 if (atEntry && atEntry->ContinentID == Map)
7321 return &itr->second;
7322 }
7323 return nullptr;
7324}
7325
7330{
7331 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7332 {
7333 if (itr->second.target_mapId == Map)
7334 {
7335 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7336 if (atEntry)
7337 return &itr->second;
7338 }
7339 }
7340 return nullptr;
7341}
7342
7344{
7345 QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
7346 if (result)
7347 GetGenerator<HighGuid::Player>().Set((*result)[0].GetUInt32() + 1);
7348
7349 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
7350 if (result)
7351 GetGenerator<HighGuid::Item>().Set((*result)[0].GetUInt32() + 1);
7352
7353 // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
7354 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7355 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7356 CharacterDatabase.PExecute("DELETE a, ab FROM auctionhouse a LEFT JOIN auctionbidders ab ON ab.id = a.id WHERE itemguid >= '{}'",
7357 GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7358 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7359
7360 result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
7361 if (result)
7362 GetGenerator<HighGuid::Mo_Transport>().Set((*result)[0].GetUInt32() + 1);
7363
7364 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
7365 if (result)
7366 _auctionId = (*result)[0].GetUInt32()+1;
7367
7368 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
7369 if (result)
7370 _mailId = (*result)[0].GetUInt32()+1;
7371
7372 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
7373 if (result)
7374 sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
7375
7376 result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
7377 if (result)
7378 _equipmentSetGuid = (*result)[0].GetUInt64()+1;
7379
7380 result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
7381 if (result)
7382 sGuildMgr->SetNextGuildId((*result)[0].GetUInt32()+1);
7383
7384 result = CharacterDatabase.Query("SELECT MAX(guid) FROM `groups`");
7385 if (result)
7386 sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
7387
7388 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
7389 if (result)
7390 _creatureSpawnId = (*result)[0].GetUInt32() + 1;
7391
7392 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
7393 if (result)
7394 _gameObjectSpawnId = (*result)[0].GetUInt32() + 1;
7395}
7396
7398{
7399 auto itr = _guidGenerators.find(high);
7400 if (itr == _guidGenerators.end())
7401 itr = _guidGenerators.insert(std::make_pair(high, std::make_unique<ObjectGuidGenerator>(high))).first;
7402
7403 return *itr->second;
7404}
7405
7407{
7408 if (_auctionId >= 0xFFFFFFFE)
7409 {
7410 TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7412 }
7413 return _auctionId++;
7414}
7415
7417{
7418 if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
7419 {
7420 TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7422 }
7423 return _equipmentSetGuid++;
7424}
7425
7427{
7428 if (_mailId >= 0xFFFFFFFE)
7429 {
7430 TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7432 }
7433 return _mailId++;
7434}
7435
7437{
7438 if (_hiPetNumber >= 0xFFFFFFFE)
7439 {
7440 TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7442 }
7443 return _hiPetNumber++;
7444}
7445
7447{
7448 if (_creatureSpawnId >= uint32(0xFFFFFF))
7449 {
7450 TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7452 }
7453 return _creatureSpawnId++;
7454}
7455
7457{
7458 if (_gameObjectSpawnId >= uint32(0xFFFFFF))
7459 {
7460 TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7462 }
7463 return _gameObjectSpawnId++;
7464}
7465
7467{
7468 uint32 oldMSTime = getMSTime();
7469
7470 _gameObjectLocaleStore.clear(); // need for reload case
7471
7472 // 0 1 2 3
7473 QueryResult result = WorldDatabase.Query("SELECT entry, locale, name, castBarCaption FROM gameobject_template_locale");
7474 if (!result)
7475 return;
7476
7477 do
7478 {
7479 Field* fields = result->Fetch();
7480
7481 uint32 id = fields[0].GetUInt32();
7482 std::string localeName = fields[1].GetString();
7483
7484 LocaleConstant locale = GetLocaleByName(localeName);
7485 if (locale == LOCALE_enUS)
7486 continue;
7487
7489 AddLocaleString(fields[2].GetString(), locale, data.Name);
7490 AddLocaleString(fields[3].GetString(), locale, data.CastBarCaption);
7491 } while (result->NextRow());
7492
7493 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject_template_locale strings in {} ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
7494}
7495
7496inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7497{
7498 if (sLockStore.LookupEntry(dataN))
7499 return;
7500
7501 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but lock (Id: {}) not found.",
7502 goInfo->entry, goInfo->type, N, goInfo->door.lockId, goInfo->door.lockId);
7503}
7504
7505inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7506{
7507 if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
7508 {
7509 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
7510 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but GO (Entry {}) have not GAMEOBJECT_TYPE_TRAP ({}) type.",
7511 goInfo->entry, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
7512 }
7513}
7514
7515inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7516{
7517 if (sSpellMgr->GetSpellInfo(dataN))
7518 return;
7519
7520 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but Spell (Entry {}) not exist.",
7521 goInfo->entry, goInfo->type, N, dataN, dataN);
7522}
7523
7524inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N)
7525{
7527 return;
7528
7529 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but correct chair height in range 0..{}.",
7531
7532 // prevent client and server unexpected work
7533 dataN = 0;
7534}
7535
7537{
7538 // 0/1 correct values
7539 if (dataN <= 1)
7540 return;
7541
7542 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
7543}
7544
7545inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7546{
7547 // 0/1 correct values
7548 if (dataN <= 1)
7549 return;
7550
7551 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) consumable field value.",
7552 goInfo->entry, goInfo->type, N, dataN);
7553}
7554
7556{
7557 uint32 oldMSTime = getMSTime();
7558
7559 // 0 1 2 3 4 5 6 7
7560 QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, "
7561 // 8 9 10 11 12 13 14 15 16 17 18 19 20
7562 "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, "
7563 // 21 22 23 24 25 26 27 28 29 30 31 32 33 34
7564 "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, AIName, ScriptName, StringId "
7565 "FROM gameobject_template");
7566
7567 if (!result)
7568 {
7569 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
7570 return;
7571 }
7572
7573 _gameObjectTemplateStore.reserve(result->GetRowCount());
7574 do
7575 {
7576 Field* fields = result->Fetch();
7577
7578 uint32 entry = fields[0].GetUInt32();
7579
7581 got.entry = entry;
7582 got.type = uint32(fields[1].GetUInt8());
7583 got.displayId = fields[2].GetUInt32();
7584 got.name = fields[3].GetString();
7585 got.IconName = fields[4].GetString();
7586 got.castBarCaption = fields[5].GetString();
7587 got.unk1 = fields[6].GetString();
7588 got.size = fields[7].GetFloat();
7589
7590 for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
7591 got.raw.data[i] = fields[8 + i].GetInt32(); // data1 and data6 can be -1
7592
7593 got.AIName = fields[32].GetString();
7594 got.ScriptId = GetScriptId(fields[33].GetString());
7595 got.StringId = fields[34].GetString();
7596
7597 // Checks
7598 if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
7599 {
7600 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has non-registered `AIName` '{}' set, removing", got.entry, got.AIName);
7601 got.AIName.clear();
7602 }
7603
7604 switch (got.type)
7605 {
7606 case GAMEOBJECT_TYPE_DOOR: //0
7607 {
7608 if (got.door.lockId)
7609 CheckGOLockId(&got, got.door.lockId, 1);
7611 break;
7612 }
7613 case GAMEOBJECT_TYPE_BUTTON: //1
7614 {
7615 if (got.button.lockId)
7616 CheckGOLockId(&got, got.button.lockId, 1);
7618 break;
7619 }
7621 {
7622 if (got.questgiver.lockId)
7623 CheckGOLockId(&got, got.questgiver.lockId, 0);
7625 break;
7626 }
7627 case GAMEOBJECT_TYPE_CHEST: //3
7628 {
7629 if (got.chest.lockId)
7630 CheckGOLockId(&got, got.chest.lockId, 0);
7631
7632 CheckGOConsumable(&got, got.chest.consumable, 3);
7633
7634 if (got.chest.linkedTrapId) // linked trap
7636 break;
7637 }
7638 case GAMEOBJECT_TYPE_TRAP: //6
7639 {
7640 if (got.trap.lockId)
7641 CheckGOLockId(&got, got.trap.lockId, 0);
7642 break;
7643 }
7644 case GAMEOBJECT_TYPE_CHAIR: //7
7646 break;
7648 {
7649 if (got.spellFocus.focusId)
7650 {
7651 if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.focusId))
7652 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but SpellFocus (Id: {}) not exist.",
7653 entry, got.type, got.spellFocus.focusId, got.spellFocus.focusId);
7654 }
7655
7656 if (got.spellFocus.linkedTrapId) // linked trap
7658 break;
7659 }
7660 case GAMEOBJECT_TYPE_GOOBER: //10
7661 {
7662 if (got.goober.lockId)
7663 CheckGOLockId(&got, got.goober.lockId, 0);
7664
7665 CheckGOConsumable(&got, got.goober.consumable, 3);
7666
7667 if (got.goober.pageId) // pageId
7668 {
7669 if (!GetPageText(got.goober.pageId))
7670 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data7={} but PageText (Entry {}) not exist.",
7671 entry, got.type, got.goober.pageId, got.goober.pageId);
7672 }
7674 if (got.goober.linkedTrapId) // linked trap
7676 break;
7677 }
7679 {
7680 if (got.areadamage.lockId)
7681 CheckGOLockId(&got, got.areadamage.lockId, 0);
7682 break;
7683 }
7684 case GAMEOBJECT_TYPE_CAMERA: //13
7685 {
7686 if (got.camera.lockId)
7687 CheckGOLockId(&got, got.camera.lockId, 0);
7688 break;
7689 }
7691 {
7692 if (got.moTransport.taxiPathId)
7693 {
7695 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but TaxiPath (Id: {}) not exist.",
7696 entry, got.type, got.moTransport.taxiPathId, got.moTransport.taxiPathId);
7697 }
7698 if (uint32 transportMap = got.moTransport.mapID)
7699 _transportMaps.insert(transportMap);
7700 break;
7701 }
7702 case GAMEOBJECT_TYPE_RITUAL: //18
7703 break;
7705 {
7706 // always must have spell
7707 CheckGOSpellId(&got, got.spellcaster.spellId, 0);
7708 break;
7709 }
7710 case GAMEOBJECT_TYPE_FLAGSTAND: //24
7711 {
7712 if (got.flagstand.lockId)
7713 CheckGOLockId(&got, got.flagstand.lockId, 0);
7715 break;
7716 }
7718 {
7719 if (got.fishinghole.lockId)
7720 CheckGOLockId(&got, got.fishinghole.lockId, 4);
7721 break;
7722 }
7723 case GAMEOBJECT_TYPE_FLAGDROP: //26
7724 {
7725 if (got.flagdrop.lockId)
7726 CheckGOLockId(&got, got.flagdrop.lockId, 0);
7728 break;
7729 }
7732 break;
7733 }
7734 } while (result->NextRow());
7735
7736 TC_LOG_INFO("server.loading", ">> Loaded {} game object templates in {} ms", _gameObjectTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
7737}
7738
7740{
7741 uint32 oldMSTime = getMSTime();
7742
7743 // 0 1 2 3 4 5 6 7 8
7744 QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3 FROM gameobject_template_addon");
7745
7746 if (!result)
7747 {
7748 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty.");
7749 return;
7750 }
7751
7752 uint32 count = 0;
7753 do
7754 {
7755 Field* fields = result->Fetch();
7756
7757 uint32 entry = fields[0].GetUInt32();
7758
7759 GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry);
7760 if (!got)
7761 {
7762 TC_LOG_ERROR("sql.sql", "GameObject template (Entry: {}) does not exist but has a record in `gameobject_template_addon`", entry);
7763 continue;
7764 }
7765
7767 gameObjectAddon.Faction = uint32(fields[1].GetUInt16());
7768 gameObjectAddon.Flags = fields[2].GetUInt32();
7769 gameObjectAddon.Mingold = fields[3].GetUInt32();
7770 gameObjectAddon.Maxgold = fields[4].GetUInt32();
7771
7772 for (uint32 i = 0; i < gameObjectAddon.artKits.size(); ++i)
7773 {
7774 uint32 artKitID = fields[5 + i].GetUInt32();
7775 if (!artKitID)
7776 continue;
7777
7778 if (!sGameObjectArtKitStore.LookupEntry(artKitID))
7779 {
7780 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` ({}) defined, set to zero instead.", entry, i, artKitID);
7781 continue;
7782 }
7783
7784 gameObjectAddon.artKits[i] = artKitID;
7785 }
7786
7787 // checks
7788 if (gameObjectAddon.Faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.Faction))
7789 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid faction ({}) defined in `gameobject_template_addon`.", entry, gameObjectAddon.Faction);
7790
7791 if (gameObjectAddon.Maxgold > 0)
7792 {
7793 switch (got->type)
7794 {
7797 break;
7798 default:
7799 TC_LOG_ERROR("sql.sql", "GameObject (Entry {} GoType: {}) cannot be looted but has maxgold set in `gameobject_template_addon`.", entry, got->type);
7800 break;
7801 }
7802 }
7803
7804 ++count;
7805 }
7806 while (result->NextRow());
7807
7808 TC_LOG_INFO("server.loading", ">> Loaded {} game object template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7809}
7810
7812{
7813 uint32 oldMSTime = getMSTime();
7814
7815 // 0 1 2
7816 QueryResult result = WorldDatabase.Query("SELECT spawnId, faction, flags FROM gameobject_overrides");
7817 if (!result)
7818 {
7819 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject faction and flags overrides. DB table `gameobject_overrides` is empty.");
7820 return;
7821 }
7822
7823 uint32 count = 0;
7824 do
7825 {
7826 Field* fields = result->Fetch();
7827
7828 ObjectGuid::LowType spawnId = fields[0].GetUInt32();
7829 GameObjectData const* goData = GetGameObjectData(spawnId);
7830 if (!goData)
7831 {
7832 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) does not exist but has a record in `gameobject_overrides`", spawnId);
7833 continue;
7834 }
7835
7836 GameObjectOverride& gameObjectOverride = _gameObjectOverrideStore[spawnId];
7837 gameObjectOverride.Faction = fields[1].GetUInt16();
7838 gameObjectOverride.Flags = fields[2].GetUInt32();
7839
7840 if (gameObjectOverride.Faction && !sFactionTemplateStore.LookupEntry(gameObjectOverride.Faction))
7841 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) has invalid faction ({}) defined in `gameobject_overrides`.", spawnId, gameObjectOverride.Faction);
7842
7843 ++count;
7844 } while (result->NextRow());
7845
7846 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject faction and flags overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7847}
7848
7850{
7851 uint32 oldMSTime = getMSTime();
7852
7853 QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
7854
7855 if (!result)
7856 {
7857 TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
7858 return;
7859 }
7860
7861 uint32 count = 0;
7862
7863 do
7864 {
7865 Field* fields = result->Fetch();
7866 uint8 level = fields[0].GetUInt8();
7867 uint32 basexp = fields[1].GetInt32();
7868 _baseXPTable[level] = basexp;
7869 ++count;
7870 }
7871 while (result->NextRow());
7872
7873 TC_LOG_INFO("server.loading", ">> Loaded {} BaseXP definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7874}
7875
7877{
7878 return _baseXPTable[level] ? _baseXPTable[level] : 0;
7879}
7880
7882{
7883 if (level < _playerXPperLevel.size())
7884 return _playerXPperLevel[level];
7885 return 0;
7886}
7887
7889{
7890 uint32 oldMSTime = getMSTime();
7891 // 0 1 2
7892 QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
7893
7894 if (!result)
7895 {
7896 TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
7897 return;
7898 }
7899
7900 uint32 count = 0;
7901
7902 do
7903 {
7904 Field* fields = result->Fetch();
7905 std::string word = fields[0].GetString();
7906 uint32 entry = fields[1].GetUInt32();
7907 bool half = fields[2].GetBool();
7908 if (half)
7909 _petHalfName1[entry].push_back(word);
7910 else
7911 _petHalfName0[entry].push_back(word);
7912 ++count;
7913 }
7914 while (result->NextRow());
7915
7916 TC_LOG_INFO("server.loading", ">> Loaded {} pet name parts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7917}
7918
7920{
7921 uint32 oldMSTime = getMSTime();
7922
7923 QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
7924 if (result)
7925 {
7926 Field* fields = result->Fetch();
7927 _hiPetNumber = fields[0].GetUInt32()+1;
7928 }
7929
7930 TC_LOG_INFO("server.loading", ">> Loaded the max pet number: {} in {} ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
7931}
7932
7934{
7935 std::vector<std::string>& list0 = _petHalfName0[entry];
7936 std::vector<std::string>& list1 = _petHalfName1[entry];
7937
7938 if (list0.empty() || list1.empty())
7939 {
7940 CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
7941 if (!cinfo)
7942 return std::string();
7943
7944 char const* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
7945 if (petname)
7946 return std::string(petname);
7947 else
7948 return cinfo->Name;
7949 }
7950
7951 return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
7952}
7953
7955{
7956 uint32 oldMSTime = getMSTime();
7957
7958 _repRewardRateStore.clear(); // for reload case
7959
7960 uint32 count = 0; // 0 1 2 3 4 5 6 7
7961 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");
7962 if (!result)
7963 {
7964 TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
7965 return;
7966 }
7967
7968 do
7969 {
7970 Field* fields = result->Fetch();
7971
7972 uint32 factionId = fields[0].GetUInt32();
7973
7974 RepRewardRate repRate;
7975
7976 repRate.questRate = fields[1].GetFloat();
7977 repRate.questDailyRate = fields[2].GetFloat();
7978 repRate.questWeeklyRate = fields[3].GetFloat();
7979 repRate.questMonthlyRate = fields[4].GetFloat();
7980 repRate.questRepeatableRate = fields[5].GetFloat();
7981 repRate.creatureRate = fields[6].GetFloat();
7982 repRate.spellRate = fields[7].GetFloat();
7983
7984 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
7985 if (!factionEntry)
7986 {
7987 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_reward_rate`", factionId);
7988 continue;
7989 }
7990
7991 if (repRate.questRate < 0.0f)
7992 {
7993 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate {}, skipping data for faction {}", repRate.questRate, factionId);
7994 continue;
7995 }
7996
7997 if (repRate.questDailyRate < 0.0f)
7998 {
7999 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_daily_rate with invalid rate {}, skipping data for faction {}", repRate.questDailyRate, factionId);
8000 continue;
8001 }
8002
8003 if (repRate.questWeeklyRate < 0.0f)
8004 {
8005 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_weekly_rate with invalid rate {}, skipping data for faction {}", repRate.questWeeklyRate, factionId);
8006 continue;
8007 }
8008
8009 if (repRate.questMonthlyRate < 0.0f)
8010 {
8011 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_monthly_rate with invalid rate {}, skipping data for faction {}", repRate.questMonthlyRate, factionId);
8012 continue;
8013 }
8014
8015 if (repRate.questRepeatableRate < 0.0f)
8016 {
8017 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_repeatable_rate with invalid rate {}, skipping data for faction {}", repRate.questRepeatableRate, factionId);
8018 continue;
8019 }
8020
8021 if (repRate.creatureRate < 0.0f)
8022 {
8023 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate {}, skipping data for faction {}", repRate.creatureRate, factionId);
8024 continue;
8025 }
8026
8027 if (repRate.spellRate < 0.0f)
8028 {
8029 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate {}, skipping data for faction {}", repRate.spellRate, factionId);
8030 continue;
8031 }
8032
8033 _repRewardRateStore[factionId] = repRate;
8034
8035 ++count;
8036 }
8037 while (result->NextRow());
8038
8039 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_reward_rate in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8040}
8041
8043{
8044 uint32 oldMSTime = getMSTime();
8045
8046 // For reload case
8047 _repOnKillStore.clear();
8048
8049 uint32 count = 0;
8050
8051 // 0 1 2
8052 QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
8053 // 3 4 5 6 7 8 9
8054 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
8055 "FROM creature_onkill_reputation");
8056
8057 if (!result)
8058 {
8059 TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
8060 return;
8061 }
8062
8063 do
8064 {
8065 Field* fields = result->Fetch();
8066
8067 uint32 creature_id = fields[0].GetUInt32();
8068
8069 ReputationOnKillEntry repOnKill;
8070 repOnKill.RepFaction1 = fields[1].GetInt16();
8071 repOnKill.RepFaction2 = fields[2].GetInt16();
8072 repOnKill.IsTeamAward1 = fields[3].GetBool();
8073 repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
8074 repOnKill.RepValue1 = fields[5].GetInt32();
8075 repOnKill.IsTeamAward2 = fields[6].GetBool();
8076 repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
8077 repOnKill.RepValue2 = fields[8].GetInt32();
8078 repOnKill.TeamDependent = fields[9].GetBool();
8079
8080 if (!GetCreatureTemplate(creature_id))
8081 {
8082 TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry ({}), skipped", creature_id);
8083 continue;
8084 }
8085
8086 if (repOnKill.RepFaction1)
8087 {
8088 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
8089 if (!factionEntry1)
8090 {
8091 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
8092 continue;
8093 }
8094 }
8095
8096 if (repOnKill.RepFaction2)
8097 {
8098 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
8099 if (!factionEntry2)
8100 {
8101 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
8102 continue;
8103 }
8104 }
8105
8106 _repOnKillStore[creature_id] = repOnKill;
8107
8108 ++count;
8109 } while (result->NextRow());
8110
8111 TC_LOG_INFO("server.loading", ">> Loaded {} creature award reputation definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8112}
8113
8115{
8116 uint32 oldMSTime = getMSTime();
8117
8118 _repSpilloverTemplateStore.clear(); // for reload case
8119
8120 uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12
8121 QueryResult result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4 FROM reputation_spillover_template");
8122
8123 if (!result)
8124 {
8125 TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
8126 return;
8127 }
8128
8129 do
8130 {
8131 Field* fields = result->Fetch();
8132
8133 uint32 factionId = fields[0].GetUInt16();
8134
8135 RepSpilloverTemplate repTemplate;
8136
8137 repTemplate.faction[0] = fields[1].GetUInt16();
8138 repTemplate.faction_rate[0] = fields[2].GetFloat();
8139 repTemplate.faction_rank[0] = fields[3].GetUInt8();
8140 repTemplate.faction[1] = fields[4].GetUInt16();
8141 repTemplate.faction_rate[1] = fields[5].GetFloat();
8142 repTemplate.faction_rank[1] = fields[6].GetUInt8();
8143 repTemplate.faction[2] = fields[7].GetUInt16();
8144 repTemplate.faction_rate[2] = fields[8].GetFloat();
8145 repTemplate.faction_rank[2] = fields[9].GetUInt8();
8146 repTemplate.faction[3] = fields[10].GetUInt16();
8147 repTemplate.faction_rate[3] = fields[11].GetFloat();
8148 repTemplate.faction_rank[3] = fields[12].GetUInt8();
8149
8150 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8151
8152 if (!factionEntry)
8153 {
8154 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_spillover_template`", factionId);
8155 continue;
8156 }
8157
8158 if (factionEntry->ParentFactionID == 0)
8159 {
8160 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} in `reputation_spillover_template` does not belong to any team, skipping", factionId);
8161 continue;
8162 }
8163
8164 bool invalidSpilloverFaction = false;
8165 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
8166 {
8167 if (repTemplate.faction[i])
8168 {
8169 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
8170
8171 if (!factionSpillover)
8172 {
8173 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);
8174 invalidSpilloverFaction = true;
8175 break;
8176 }
8177
8178 if (!factionSpillover->CanHaveReputation())
8179 {
8180 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);
8181 invalidSpilloverFaction = true;
8182 break;
8183 }
8184
8185 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
8186 {
8187 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]);
8188 invalidSpilloverFaction = true;
8189 break;
8190 }
8191 }
8192 }
8193
8194 if (invalidSpilloverFaction)
8195 continue;
8196
8197 _repSpilloverTemplateStore[factionId] = repTemplate;
8198
8199 ++count;
8200 }
8201 while (result->NextRow());
8202
8203 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_spillover_template in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8204}
8205
8207{
8208 uint32 oldMSTime = getMSTime();
8209
8210 _pointsOfInterestStore.clear(); // need for reload case
8211
8212 uint32 count = 0;
8213
8214 // 0 1 2 3 4 5 6
8215 QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, Icon, Flags, Importance, Name FROM points_of_interest");
8216
8217 if (!result)
8218 {
8219 TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
8220 return;
8221 }
8222
8223 do
8224 {
8225 Field* fields = result->Fetch();
8226
8227 uint32 id = fields[0].GetUInt32();
8228
8229 PointOfInterest pointOfInterest;
8230 pointOfInterest.ID = id;
8231 pointOfInterest.PositionX = fields[1].GetFloat();
8232 pointOfInterest.PositionY = fields[2].GetFloat();
8233 pointOfInterest.Icon = fields[3].GetUInt32();
8234 pointOfInterest.Flags = fields[4].GetUInt32();
8235 pointOfInterest.Importance = fields[5].GetUInt32();
8236 pointOfInterest.Name = fields[6].GetString();
8237
8238 if (!Trinity::IsValidMapCoord(pointOfInterest.PositionX, pointOfInterest.PositionY))
8239 {
8240 TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (ID: {}) have invalid coordinates (X: {} Y: {}), ignored.", id, pointOfInterest.PositionX, pointOfInterest.PositionY);
8241 continue;
8242 }
8243
8244 _pointsOfInterestStore[id] = pointOfInterest;
8245
8246 ++count;
8247 } while (result->NextRow());
8248
8249 TC_LOG_INFO("server.loading", ">> Loaded {} Points of Interest definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8250}
8251
8253{
8254 uint32 oldMSTime = getMSTime();
8255
8256 _questPOIStore.clear(); // need for reload case
8257
8258 // 0 1 2 3 4 5 6 7
8259 QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi");
8260 if (!result)
8261 {
8262 TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
8263 return;
8264 }
8265
8266 _questPOIStore.reserve(result->GetRowCount());
8267
8268 // 0 1 2 3
8269 QueryResult points = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y FROM quest_poi_points ORDER BY QuestID DESC, Idx2");
8270
8271 std::vector<std::vector<std::vector<QuestPOIBlobPoint>>> POIs;
8272 if (points)
8273 {
8274 // The first result should have the highest questId
8275 Field* fields = points->Fetch();
8276 uint32 const maxQuestPOIId = fields[0].GetUInt32();
8277 POIs.resize(maxQuestPOIId + 1);
8278
8279 do
8280 {
8281 fields = points->Fetch();
8282
8283 uint32 questId = fields[0].GetUInt32();
8284 uint32 id = fields[1].GetUInt32();
8285 int32 x = fields[2].GetInt32();
8286 int32 y = fields[3].GetInt32();
8287
8288 if (POIs[questId].size() <= id + 1)
8289 POIs[questId].resize(id + 10);
8290
8291 QuestPOIBlobPoint point;
8292 point.X = x;
8293 point.Y = y;
8294
8295 POIs[questId][id].push_back(point);
8296 } while (points->NextRow());
8297 }
8298
8299 do
8300 {
8301 Field* fields = result->Fetch();
8302
8303 uint32 questId = fields[0].GetUInt32();
8304 uint32 id = fields[1].GetUInt32();
8305 int32 objIndex = fields[2].GetInt32();
8306 uint32 mapId = fields[3].GetUInt32();
8307 uint32 WorldMapAreaId = fields[4].GetUInt32();
8308 uint32 FloorId = fields[5].GetUInt32();
8309 uint32 unk3 = fields[6].GetUInt32();
8310 uint32 unk4 = fields[7].GetUInt32();
8311
8312 QuestPOIBlobData POI;
8313 POI.BlobIndex = id;
8314 POI.ObjectiveIndex = objIndex;
8315 POI.MapID = mapId;
8316 POI.WorldMapAreaID = WorldMapAreaId;
8317 POI.Floor = FloorId;
8318 POI.Unk3 = unk3;
8319 POI.Unk4 = unk4;
8320
8321 if (questId < POIs.size() && id < POIs[questId].size())
8322 {
8323 POI.QuestPOIBlobPointStats = POIs[questId][id];
8324
8325 auto itr = _questPOIStore.find(questId);
8326 if (itr == _questPOIStore.end())
8327 {
8328 QuestPOIWrapper wrapper;
8329 QuestPOIData data;
8330 data.QuestID = questId;
8331 wrapper.POIData = data;
8332
8333 std::tie(itr, std::ignore) = _questPOIStore.emplace(questId, std::move(wrapper));
8334 }
8335
8336 itr->second.POIData.QuestPOIBlobDataStats.push_back(POI);
8337 }
8338 else
8339 TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest {} POI id {}", questId, id);
8340 } while (result->NextRow());
8341
8342 TC_LOG_INFO("server.loading", ">> Loaded {} quest POI definitions in {} ms", _questPOIStore.size(), GetMSTimeDiffToNow(oldMSTime));
8343}
8344
8346{
8347 uint32 oldMSTime = getMSTime();
8348
8349 _spellClickInfoStore.clear();
8350 // 0 1 2 3
8351 QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
8352
8353 if (!result)
8354 {
8355 TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
8356 return;
8357 }
8358
8359 uint32 count = 0;
8360
8361 do
8362 {
8363 Field* fields = result->Fetch();
8364
8365 uint32 npc_entry = fields[0].GetUInt32();
8366 CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
8367 if (!cInfo)
8368 {
8369 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template {}. Skipping entry.", npc_entry);
8370 continue;
8371 }
8372
8373 uint32 spellid = fields[1].GetUInt32();
8374 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid);
8375 if (!spellinfo)
8376 {
8377 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown spellid {}. Skipping entry.", npc_entry, spellid);
8378 continue;
8379 }
8380
8381 uint8 userType = fields[3].GetUInt16();
8382 if (userType >= SPELL_CLICK_USER_MAX)
8383 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown user type {}. Skipping entry.", npc_entry, uint32(userType));
8384
8385 uint8 castFlags = fields[2].GetUInt8();
8386 SpellClickInfo info;
8387 info.spellId = spellid;
8388 info.castFlags = castFlags;
8389 info.userType = SpellClickUserTypes(userType);
8390 _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
8391
8392 ++count;
8393 }
8394 while (result->NextRow());
8395
8396 // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
8397 // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
8398 for (auto& creatureTemplatePair : _creatureTemplateStore)
8399 {
8400 if ((creatureTemplatePair.second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && !_spellClickInfoStore.count(creatureTemplatePair.first))
8401 {
8402 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);
8403 creatureTemplatePair.second.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
8404 }
8405 }
8406
8407 TC_LOG_INFO("server.loading", ">> Loaded {} spellclick definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8408}
8409
8411{
8412 // remove mapid*cellid -> guid_set map
8413 CreatureData const* data = GetCreatureData(guid);
8414 if (data)
8415 {
8416 RemoveCreatureFromGrid(guid, data);
8417 OnDeleteSpawnData(data);
8418 }
8419
8420 _creatureDataStore.erase(guid);
8421}
8422
8424{
8425 // remove mapid*cellid -> guid_set map
8426 GameObjectData const* data = GetGameObjectData(guid);
8427 if (data)
8428 {
8429 RemoveGameobjectFromGrid(guid, data);
8430 OnDeleteSpawnData(data);
8431 }
8432
8433 _gameObjectDataStore.erase(guid);
8434}
8435
8436void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, std::string const& table)
8437{
8438 uint32 oldMSTime = getMSTime();
8439
8440 map.clear(); // need for reload case
8441
8442 uint32 count = 0;
8443
8444 QueryResult result = WorldDatabase.PQuery("SELECT id, quest FROM {}", table);
8445
8446 if (!result)
8447 {
8448 TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `{}`, table is empty.", table);
8449 return;
8450 }
8451
8452 do
8453 {
8454 uint32 id = result->Fetch()[0].GetUInt32();
8455 uint32 quest = result->Fetch()[1].GetUInt32();
8456
8457 if (!_questTemplates.count(quest))
8458 {
8459 TC_LOG_ERROR("sql.sql", "Table `{}`: Quest {} listed for entry {} does not exist.", table, quest, id);
8460 continue;
8461 }
8462
8463 map.insert(QuestRelations::value_type(id, quest));
8464 ++count;
8465 } while (result->NextRow());
8466
8467 TC_LOG_INFO("server.loading", ">> Loaded {} quest relations from {} in {} ms", count, table, GetMSTimeDiffToNow(oldMSTime));
8468}
8469
8471{
8472 LoadQuestRelationsHelper(_goQuestRelations, "gameobject_queststarter");
8473
8474 for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
8475 {
8476 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8477 if (!goInfo)
8478 TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8479 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8480 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);
8481 }
8482}
8483
8485{
8486 LoadQuestRelationsHelper(_goQuestInvolvedRelations, "gameobject_questender");
8487
8488 for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
8489 {
8490 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8491 if (!goInfo)
8492 TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8493 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8494 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);
8495 }
8496}
8497
8499{
8500 LoadQuestRelationsHelper(_creatureQuestRelations, "creature_queststarter");
8501
8502 for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
8503 {
8504 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8505 if (!cInfo)
8506 TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8507 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8508 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);
8509 }
8510}
8511
8513{
8515
8516 for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
8517 {
8518 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8519 if (!cInfo)
8520 TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8521 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8522 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);
8523 }
8524}
8525
8527{
8528 while ((_it != _end) && !Quest::IsTakingQuestEnabled(_it->second))
8529 ++_it;
8530}
8531
8533{
8534 return (std::find_if(_begin, _end, [questId](QuestRelations::value_type const& pair) { return (pair.second == questId); }) != _end) && (!_onlyActive || Quest::IsTakingQuestEnabled(questId));
8535}
8536
8538{
8539 uint32 oldMSTime = getMSTime();
8540
8541 _reservedNamesStore.clear(); // need for reload case
8542
8543 QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
8544
8545 if (!result)
8546 {
8547 TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
8548 return;
8549 }
8550
8551 uint32 count = 0;
8552
8553 Field* fields;
8554 do
8555 {
8556 fields = result->Fetch();
8557 std::string name= fields[0].GetString();
8558
8559 std::wstring wstr;
8560 if (!Utf8toWStr (name, wstr))
8561 {
8562 TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: {}", name);
8563 continue;
8564 }
8565
8566 wstrToLower(wstr);
8567
8568 _reservedNamesStore.insert(wstr);
8569 ++count;
8570 }
8571 while (result->NextRow());
8572
8573 TC_LOG_INFO("server.loading", ">> Loaded {} reserved player names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8574}
8575
8576bool ObjectMgr::IsReservedName(std::string_view name) const
8577{
8578 std::wstring wstr;
8579 if (!Utf8toWStr (name, wstr))
8580 return false;
8581
8582 wstrToLower(wstr);
8583
8584 return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
8585}
8586
8588{
8591 LT_CYRILLIC = 0x0002,
8593 LT_ANY = 0xFFFF
8595
8597{
8598 switch (sWorld->getIntConfig(CONFIG_REALM_ZONE))
8599 {
8600 case REALM_ZONE_UNKNOWN: // any language
8604 return LT_ANY;
8605 case REALM_ZONE_UNITED_STATES: // extended-Latin
8606 case REALM_ZONE_OCEANIC:
8608 case REALM_ZONE_ENGLISH:
8609 case REALM_ZONE_GERMAN:
8610 case REALM_ZONE_FRENCH:
8611 case REALM_ZONE_SPANISH:
8612 return LT_EXTENDEN_LATIN;
8613 case REALM_ZONE_KOREA: // East-Asian
8614 case REALM_ZONE_TAIWAN:
8615 case REALM_ZONE_CHINA:
8616 return LT_EAST_ASIA;
8617 case REALM_ZONE_RUSSIAN: // Cyrillic
8618 return LT_CYRILLIC;
8619 default:
8620 return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
8621 }
8622}
8623
8624bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
8625{
8626 if (strictMask == 0) // any language, ignore realm
8627 {
8628 if (isExtendedLatinString(wstr, numericOrSpace))
8629 return true;
8630 if (isCyrillicString(wstr, numericOrSpace))
8631 return true;
8632 if (isEastAsianString(wstr, numericOrSpace))
8633 return true;
8634 return false;
8635 }
8636
8637 if (strictMask & 0x2) // realm zone specific
8638 {
8640 if (lt & LT_EXTENDEN_LATIN)
8641 if (isExtendedLatinString(wstr, numericOrSpace))
8642 return true;
8643 if (lt & LT_CYRILLIC)
8644 if (isCyrillicString(wstr, numericOrSpace))
8645 return true;
8646 if (lt & LT_EAST_ASIA)
8647 if (isEastAsianString(wstr, numericOrSpace))
8648 return true;
8649 }
8650
8651 if (strictMask & 0x1) // basic Latin
8652 {
8653 if (isBasicLatinString(wstr, numericOrSpace))
8654 return true;
8655 }
8656
8657 return false;
8658}
8659
8660ResponseCodes ObjectMgr::CheckPlayerName(std::string_view name, LocaleConstant locale, bool create /*= false*/)
8661{
8662 std::wstring wname;
8663 if (!Utf8toWStr(name, wname))
8665
8666 if (wname.size() > MAX_PLAYER_NAME)
8667 return CHAR_NAME_TOO_LONG;
8668
8669 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
8670 if (wname.size() < minName)
8671 return CHAR_NAME_TOO_SHORT;
8672
8673 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
8674 if (!isValidString(wname, strictMask, false, create))
8676
8677 wstrToLower(wname);
8678 for (size_t i = 2; i < wname.size(); ++i)
8679 if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
8681
8682 return ValidateName(wname, locale);
8683}
8684
8685bool ObjectMgr::IsValidCharterName(std::string_view name)
8686{
8687 std::wstring wname;
8688 if (!Utf8toWStr(name, wname))
8689 return false;
8690
8691 if (wname.size() > MAX_CHARTER_NAME)
8692 return false;
8693
8694 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
8695 if (wname.size() < minName)
8696 return false;
8697
8698 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
8699
8700 return isValidString(wname, strictMask, true);
8701}
8702
8704{
8705 std::wstring wname;
8706 if (!Utf8toWStr(name, wname))
8707 return PET_NAME_INVALID;
8708
8709 if (wname.size() > MAX_PET_NAME)
8710 return PET_NAME_TOO_LONG;
8711
8712 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
8713 if (wname.size() < minName)
8714 return PET_NAME_TOO_SHORT;
8715
8716 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
8717 if (!isValidString(wname, strictMask, false))
8719
8720 switch (ValidateName(wname, locale))
8721 {
8722 case CHAR_NAME_PROFANE:
8723 return PET_NAME_PROFANE;
8724 case CHAR_NAME_RESERVED:
8725 return PET_NAME_RESERVED;
8726 case RESPONSE_FAILURE:
8727 return PET_NAME_INVALID;
8728 default:
8729 break;
8730 }
8731 return PET_NAME_SUCCESS;
8732}
8733
8735{
8736 uint32 oldMSTime = getMSTime();
8737
8738 _gameObjectForQuestStore.clear(); // need for reload case
8739
8740 if (_gameObjectTemplateStore.empty())
8741 {
8742 TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
8743 return;
8744 }
8745
8746 uint32 count = 0;
8747
8748 // collect GO entries for GO that must activated
8749 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
8750 {
8751 switch (gameObjectTemplatePair.second.type)
8752 {
8754 break;
8756 {
8757 // scan GO chest with loot including quest items
8758 uint32 lootId = gameObjectTemplatePair.second.GetLootId();
8759 // find quest loot for GO
8760 if (gameObjectTemplatePair.second.chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(lootId))
8761 break;
8762 continue;
8763 }
8765 {
8766 if (gameObjectTemplatePair.second._generic.questID > 0) //quests objects
8767 break;
8768 continue;
8769 }
8771 {
8772 if (gameObjectTemplatePair.second.goober.questId > 0) //quests objects
8773 break;
8774 continue;
8775 }
8776 default:
8777 continue;
8778 }
8779
8780 _gameObjectForQuestStore.insert(gameObjectTemplatePair.first);
8781 ++count;
8782 }
8783
8784 TC_LOG_INFO("server.loading", ">> Loaded {} GameObjects for quests in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8785}
8786
8788{
8789 uint32 oldMSTime = getMSTime();
8790
8791 _trinityStringStore.clear(); // for reload case
8792
8793 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");
8794 if (!result)
8795 {
8796 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.");
8797 return false;
8798 }
8799
8800 do
8801 {
8802 Field* fields = result->Fetch();
8803
8804 uint32 entry = fields[0].GetUInt32();
8805
8806 TrinityString& data = _trinityStringStore[entry];
8807
8808 data.Content.resize(DEFAULT_LOCALE + 1);
8809
8810 for (int8 i = TOTAL_LOCALES - 1; i >= 0; --i)
8811 AddLocaleString(fields[i + 1].GetString(), LocaleConstant(i), data.Content);
8812 }
8813 while (result->NextRow());
8814
8815 TC_LOG_INFO("server.loading", ">> Loaded {} trinity strings in {} ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
8816 return true;
8817}
8818
8819char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
8820{
8821 if (TrinityString const* ts = GetTrinityString(entry))
8822 {
8823 if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
8824 return ts->Content[locale].c_str();
8825 return ts->Content[DEFAULT_LOCALE].c_str();
8826 }
8827
8828 TC_LOG_ERROR("sql.sql", "Trinity string entry {} not found in DB.", entry);
8829 return "<error>";
8830}
8831
8833{
8834 uint32 oldMSTime = getMSTime();
8835
8836 _fishingBaseForAreaStore.clear(); // for reload case
8837
8838 QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
8839
8840 if (!result)
8841 {
8842 TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
8843 return;
8844 }
8845
8846 uint32 count = 0;
8847
8848 do
8849 {
8850 Field* fields = result->Fetch();
8851 uint32 entry = fields[0].GetUInt32();
8852 int32 skill = fields[1].GetInt16();
8853
8854 AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
8855 if (!fArea)
8856 {
8857 TC_LOG_ERROR("sql.sql", "AreaId {} defined in `skill_fishing_base_level` does not exist", entry);
8858 continue;
8859 }
8860
8861 _fishingBaseForAreaStore[entry] = skill;
8862 ++count;
8863 }
8864 while (result->NextRow());
8865
8866 TC_LOG_INFO("server.loading", ">> Loaded {} areas for fishing base skill level in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8867}
8868
8869bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
8870{
8871 // get main part of the name
8872 std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
8873 // prepare flags
8874 bool x = true;
8875 bool y = true;
8876
8877 // check declined names
8878 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
8879 {
8880 std::wstring wname;
8881 if (!Utf8toWStr(names.name[i], wname))
8882 return false;
8883
8884 if (mainpart != GetMainPartOfName(wname, i+1))
8885 x = false;
8886
8887 if (w_ownname != wname)
8888 y = false;
8889 }
8890 return (x || y);
8891}
8892
8894{
8895 AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
8896 if (i!= _areaTriggerScriptStore.end())
8897 return i->second;
8898 return 0;
8899}
8900
8902{
8903 return _spellScriptsStore.equal_range(spellId);
8904}
8905
8906// this allows calculating base reputations to offline players, just by race and class
8907int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass) const
8908{
8909 if (!factionEntry)
8910 return 0;
8911
8912 uint32 raceMask = (1 << (race - 1));
8913 uint32 classMask = (1 << (playerClass-1));
8914
8915 for (uint8 i = 0; i < 4; ++i)
8916 {
8917 if ((!factionEntry->ReputationClassMask[i] ||
8918 factionEntry->ReputationClassMask[i] & classMask) &&
8919 (!factionEntry->ReputationRaceMask[i] ||
8920 factionEntry->ReputationRaceMask[i] & raceMask))
8921 return factionEntry->ReputationBase[i];
8922 }
8923
8924 return 0;
8925}
8926
8928{
8929 SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
8930 if (!skill)
8931 return SKILL_RANGE_NONE;
8932
8933 if (sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
8934 return SKILL_RANGE_RANK;
8935
8936 if (rcEntry->SkillID == SKILL_RUNEFORGING)
8937 return SKILL_RANGE_MONO;
8938
8939 switch (skill->CategoryID)
8940 {
8942 return SKILL_RANGE_MONO;
8944 return SKILL_RANGE_LANGUAGE;
8945 }
8946
8947 return SKILL_RANGE_LEVEL;
8948}
8949
8951{
8952 uint32 oldMSTime = getMSTime();
8953
8954 _gameTeleStore.clear(); // for reload case
8955
8956 // 0 1 2 3 4 5 6
8957 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
8958
8959 if (!result)
8960 {
8961 TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
8962 return;
8963 }
8964
8965 uint32 count = 0;
8966
8967 do
8968 {
8969 Field* fields = result->Fetch();
8970
8971 uint32 id = fields[0].GetUInt32();
8972
8973 GameTele gt;
8974
8975 gt.position_x = fields[1].GetFloat();
8976 gt.position_y = fields[2].GetFloat();
8977 gt.position_z = fields[3].GetFloat();
8978 gt.orientation = fields[4].GetFloat();
8979 gt.mapId = fields[5].GetUInt16();
8980 gt.name = fields[6].GetString();
8981
8983 {
8984 TC_LOG_ERROR("sql.sql", "Wrong position for id {} (name: {}) in `game_tele` table, ignoring.", id, gt.name);
8985 continue;
8986 }
8987
8988 if (!Utf8toWStr(gt.name, gt.wnameLow))
8989 {
8990 TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_tele` table, ignoring.", id);
8991 continue;
8992 }
8993
8995
8996 _gameTeleStore[id] = gt;
8997
8998 ++count;
8999 }
9000 while (result->NextRow());
9001
9002 TC_LOG_INFO("server.loading", ">> Loaded {} GameTeleports in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9003}
9004
9005GameTele const* ObjectMgr::GetGameTele(std::string_view name) const
9006{
9007 // explicit name case
9008 std::wstring wname;
9009 if (!Utf8toWStr(name, wname))
9010 return nullptr;
9011
9012 // converting string that we try to find to lower case
9013 wstrToLower(wname);
9014
9015 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
9016 GameTele const* alt = nullptr;
9017 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9018 {
9019 if (itr->second.wnameLow == wname)
9020 return &itr->second;
9021 else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
9022 alt = &itr->second;
9023 }
9024
9025 return alt;
9026}
9027
9028GameTele const* ObjectMgr::GetGameTeleExactName(std::string_view name) const
9029{
9030 // explicit name case
9031 std::wstring wname;
9032 if (!Utf8toWStr(name, wname))
9033 return nullptr;
9034
9035 // converting string that we try to find to lower case
9036 wstrToLower(wname);
9037
9038 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9039 {
9040 if (itr->second.wnameLow == wname)
9041 return &itr->second;
9042 }
9043
9044 return nullptr;
9045}
9046
9048{
9049 // find max id
9050 uint32 new_id = 0;
9051 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9052 if (itr->first > new_id)
9053 new_id = itr->first;
9054
9055 // use next
9056 ++new_id;
9057
9058 if (!Utf8toWStr(tele.name, tele.wnameLow))
9059 return false;
9060
9061 wstrToLower(tele.wnameLow);
9062
9063 _gameTeleStore[new_id] = tele;
9064
9066
9067 stmt->setUInt32(0, new_id);
9068 stmt->setFloat(1, tele.position_x);
9069 stmt->setFloat(2, tele.position_y);
9070 stmt->setFloat(3, tele.position_z);
9071 stmt->setFloat(4, tele.orientation);
9072 stmt->setUInt16(5, uint16(tele.mapId));
9073 stmt->setString(6, tele.name);
9074
9075 WorldDatabase.Execute(stmt);
9076
9077 return true;
9078}
9079
9080bool ObjectMgr::DeleteGameTele(std::string_view name)
9081{
9082 // explicit name case
9083 std::wstring wname;
9084 if (!Utf8toWStr(name, wname))
9085 return false;
9086
9087 // converting string that we try to find to lower case
9088 wstrToLower(wname);
9089
9090 for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9091 {
9092 if (itr->second.wnameLow == wname)
9093 {
9095
9096 stmt->setString(0, itr->second.name);
9097
9098 WorldDatabase.Execute(stmt);
9099
9100 _gameTeleStore.erase(itr);
9101 return true;
9102 }
9103 }
9104
9105 return false;
9106}
9107
9109{
9110 uint32 oldMSTime = getMSTime();
9111
9112 _mailLevelRewardStore.clear(); // for reload case
9113
9114 // 0 1 2 3
9115 QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
9116
9117 if (!result)
9118 {
9119 TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
9120 return;
9121 }
9122
9123 uint32 count = 0;
9124
9125 do
9126 {
9127 Field* fields = result->Fetch();
9128
9129 uint8 level = fields[0].GetUInt8();
9130 uint32 raceMask = fields[1].GetUInt32();
9131 uint32 mailTemplateId = fields[2].GetUInt32();
9132 uint32 senderEntry = fields[3].GetUInt32();
9133
9134 if (level > MAX_LEVEL)
9135 {
9136 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level {} that more supported by client ({}), ignoring.", level, MAX_LEVEL);
9137 continue;
9138 }
9139
9140 if (!(raceMask & RACEMASK_ALL_PLAYABLE))
9141 {
9142 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask ({}) for level {} that not include any player races, ignoring.", raceMask, level);
9143 continue;
9144 }
9145
9146 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
9147 {
9148 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has invalid mailTemplateId ({}) for level {} that invalid not include any player races, ignoring.", mailTemplateId, level);
9149 continue;
9150 }
9151
9152 if (!GetCreatureTemplate(senderEntry))
9153 {
9154 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);
9155 continue;
9156 }
9157
9158 _mailLevelRewardStore[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));
9159
9160 ++count;
9161 }
9162 while (result->NextRow());
9163
9164 TC_LOG_INFO("server.loading", ">> Loaded {} level dependent mail rewards in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9165}
9166
9168{
9169 uint32 oldMSTime = getMSTime();
9170
9171 // For reload case
9172 _trainers.clear();
9173
9174 std::unordered_map<int32, std::vector<Trainer::Spell>> spellsByTrainer;
9175 if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"))
9176 {
9177 do
9178 {
9179 Field* fields = trainerSpellsResult->Fetch();
9180
9181 Trainer::Spell spell;
9182 uint32 trainerId = fields[0].GetUInt32();
9183 spell.SpellId = fields[1].GetUInt32();
9184 spell.MoneyCost = fields[2].GetUInt32();
9185 spell.ReqSkillLine = fields[3].GetUInt32();
9186 spell.ReqSkillRank = fields[4].GetUInt32();
9187 spell.ReqAbility[0] = fields[5].GetUInt32();
9188 spell.ReqAbility[1] = fields[6].GetUInt32();
9189 spell.ReqAbility[2] = fields[7].GetUInt32();
9190 spell.ReqLevel = fields[8].GetUInt8();
9191
9192 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId);
9193 if (!spellInfo)
9194 {
9195 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) for TrainerId {}, ignoring", spell.SpellId, trainerId);
9196 continue;
9197 }
9198
9199 if (GetTalentSpellCost(spell.SpellId))
9200 {
9201 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) which is a talent, for TrainerId {}, ignoring", spell.SpellId, trainerId);
9202 continue;
9203 }
9204
9205 if (spell.ReqSkillLine && !sSkillLineStore.LookupEntry(spell.ReqSkillLine))
9206 {
9207 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing skill (ReqSkillLine: {}) for TrainerId {} and SpellId {}, ignoring",
9208 spell.ReqSkillLine, trainerId, spell.SpellId);
9209 continue;
9210 }
9211
9212 bool allReqValid = true;
9213 for (std::size_t i = 0; i < spell.ReqAbility.size(); ++i)
9214 {
9215 uint32 requiredSpell = spell.ReqAbility[i];
9216 if (requiredSpell && !sSpellMgr->GetSpellInfo(requiredSpell))
9217 {
9218 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (ReqAbility{}: {}) for TrainerId {} and SpellId {}, ignoring",
9219 i + 1, requiredSpell, trainerId, spell.SpellId);
9220 allReqValid = false;
9221 }
9222 }
9223
9224 if (!allReqValid)
9225 continue;
9226
9227 spellsByTrainer[trainerId].push_back(spell);
9228 } while (trainerSpellsResult->NextRow());
9229 }
9230
9231 if (QueryResult trainersResult = WorldDatabase.Query("SELECT Id, Type, Requirement, Greeting FROM trainer"))
9232 {
9233 do
9234 {
9235 Field* fields = trainersResult->Fetch();
9236
9237 uint32 trainerId = fields[0].GetUInt32();
9238 Trainer::Type trainerType = Trainer::Type(fields[1].GetUInt8());
9239 uint32 requirement = fields[2].GetUInt32();
9240 std::string greeting = fields[3].GetString();
9241 std::vector<Trainer::Spell> spells;
9242 auto spellsItr = spellsByTrainer.find(trainerId);
9243 if (spellsItr != spellsByTrainer.end())
9244 {
9245 spells = std::move(spellsItr->second);
9246 spellsByTrainer.erase(spellsItr);
9247 }
9248
9249 switch (trainerType)
9250 {
9252 case Trainer::Type::Pet:
9253 if (requirement && !sChrClassesStore.LookupEntry(requirement))
9254 {
9255 TC_LOG_ERROR("sql.sql", "Table `trainer` references non-existing class requirement {} for TrainerId {}, ignoring", requirement, trainerId);
9256 continue;
9257 }
9258 break;
9260 if (requirement && !sChrRacesStore.LookupEntry(requirement))
9261 {
9262 TC_LOG_ERROR("sql.sql", "Table `trainer` references non-existing race requirement {} for TrainerId {}, ignoring", requirement, trainerId);
9263 continue;
9264 }
9265 break;
9267 if (requirement && !sSpellMgr->GetSpellInfo(requirement))
9268 {
9269 TC_LOG_ERROR("sql.sql", "Table `trainer` references non-existing spell requirement {} for TrainerId {}, ignoring", requirement, trainerId);
9270 continue;
9271 }
9272 break;
9273 default:
9274 break;
9275 }
9276
9277 auto [it, isNew] = _trainers.emplace(std::piecewise_construct, std::forward_as_tuple(trainerId), std::forward_as_tuple(trainerId, trainerType, requirement, std::move(greeting), std::move(spells)));
9278 ASSERT(isNew);
9279 if (trainerType == Trainer::Type::Class)
9280 _classTrainers[requirement].push_back(&it->second);
9281 } while (trainersResult->NextRow());
9282 }
9283
9284 for (auto const& unusedSpells : spellsByTrainer)
9285 {
9286 for (Trainer::Spell const& unusedSpell : unusedSpells.second)
9287 {
9288 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing trainer (TrainerId: {}) for SpellId {}, ignoring", unusedSpells.first, unusedSpell.SpellId);
9289 }
9290 }
9291
9292 if (QueryResult trainerLocalesResult = WorldDatabase.Query("SELECT Id, locale, Greeting_lang FROM trainer_locale"))
9293 {
9294 do
9295 {
9296 Field* fields = trainerLocalesResult->Fetch();
9297 uint32 trainerId = fields[0].GetUInt32();
9298 std::string localeName = fields[1].GetString();
9299
9300 LocaleConstant locale = GetLocaleByName(localeName);
9301 if (locale == LOCALE_enUS)
9302 continue;
9303
9304 if (Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId))
9305 trainer->AddGreetingLocale(locale, fields[2].GetString());
9306 else
9307 TC_LOG_ERROR("sql.sql", "Table `trainer_locale` references non-existing trainer (TrainerId: {}) for locale {}, ignoring",
9308 trainerId, localeName);
9309 } while (trainerLocalesResult->NextRow());
9310 }
9311
9312 TC_LOG_INFO("server.loading", ">> Loaded {} Trainers in {} ms", _trainers.size(), GetMSTimeDiffToNow(oldMSTime));
9313}
9314
9316{
9317 uint32 oldMSTime = getMSTime();
9318
9319 _creatureDefaultTrainers.clear();
9320
9321 if (QueryResult result = WorldDatabase.Query("SELECT CreatureId, TrainerId FROM creature_default_trainer"))
9322 {
9323 do
9324 {
9325 Field* fields = result->Fetch();
9326 uint32 creatureId = fields[0].GetUInt32();
9327 uint32 trainerId = fields[1].GetUInt32();
9328
9329 if (!GetCreatureTemplate(creatureId))
9330 {
9331 TC_LOG_ERROR("sql.sql", "Table `creature_default_trainer` references non-existing creature template (CreatureId: {}), ignoring", creatureId);
9332 continue;
9333 }
9334
9335 Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId);
9336 if (!trainer)
9337 {
9338 TC_LOG_ERROR("sql.sql", "Table `creature_default_trainer` references non-existing trainer (TrainerId: {}) for CreatureId {}, ignoring", trainerId, creatureId);
9339 continue;
9340 }
9341
9342 _creatureDefaultTrainers[creatureId] = trainer;
9343
9344 } while (result->NextRow());
9345 }
9346
9347 TC_LOG_INFO("server.loading", ">> Loaded {} default trainers in {} ms", _creatureDefaultTrainers.size(), GetMSTimeDiffToNow(oldMSTime));
9348}
9349
9350uint32 ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
9351{
9352 // find all items from the reference vendor
9354 stmt->setUInt32(0, uint32(item));
9355 PreparedQueryResult result = WorldDatabase.Query(stmt);
9356
9357 if (!result)
9358 return 0;
9359
9360 uint32 count = 0;
9361 do
9362 {
9363 Field* fields = result->Fetch();
9364
9365 int32 item_id = fields[0].GetInt32();
9366
9367 // if item is a negative, its a reference
9368 if (item_id < 0)
9369 count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
9370 else
9371 {
9372 int32 maxcount = fields[1].GetUInt8();
9373 uint32 incrtime = fields[2].GetUInt32();
9374 uint32 ExtendedCost = fields[3].GetUInt32();
9375
9376 if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, nullptr, skip_vendors))
9377 continue;
9378
9379 VendorItemData& vList = _cacheVendorItemStore[vendor];
9380
9381 vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
9382 ++count;
9383 }
9384 } while (result->NextRow());
9385
9386 return count;
9387}
9388
9390{
9391 uint32 oldMSTime = getMSTime();
9392
9393 // For reload case
9394 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
9395 itr->second.Clear();
9396 _cacheVendorItemStore.clear();
9397
9398 std::set<uint32> skip_vendors;
9399
9400 QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor ORDER BY entry, slot ASC");
9401 if (!result)
9402 {
9403
9404 TC_LOG_ERROR("sql.sql", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
9405 return;
9406 }
9407
9408 uint32 count = 0;
9409
9410 do
9411 {
9412 Field* fields = result->Fetch();
9413
9414 uint32 entry = fields[0].GetUInt32();
9415 int32 item_id = fields[1].GetInt32();
9416
9417 // if item is a negative, its a reference
9418 if (item_id < 0)
9419 count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
9420 else
9421 {
9422 uint32 maxcount = fields[2].GetUInt8();
9423 uint32 incrtime = fields[3].GetUInt32();
9424 uint32 ExtendedCost = fields[4].GetUInt32();
9425
9426 if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, nullptr, &skip_vendors))
9427 continue;
9428
9429 VendorItemData& vList = _cacheVendorItemStore[entry];
9430
9431 vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
9432 ++count;
9433 }
9434 }
9435 while (result->NextRow());
9436
9437 TC_LOG_INFO("server.loading", ">> Loaded {} Vendors in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9438}
9439
9441{
9442 uint32 oldMSTime = getMSTime();
9443
9444 _gossipMenusStore.clear();
9445
9446 // 0 1
9447 QueryResult result = WorldDatabase.Query("SELECT MenuID, TextID FROM gossip_menu");
9448
9449 if (!result)
9450 {
9451 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
9452 return;
9453 }
9454
9455 do
9456 {
9457 Field* fields = result->Fetch();
9458
9459 GossipMenus gMenu;
9460
9461 gMenu.MenuID = fields[0].GetUInt16();
9462 gMenu.TextID = fields[1].GetUInt32();
9463
9464 if (!GetGossipText(gMenu.TextID))
9465 {
9466 TC_LOG_ERROR("sql.sql", "Table gossip_menu: ID {} is using non-existing TextID {}", gMenu.MenuID, gMenu.TextID);
9467 continue;
9468 }
9469
9470 _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.MenuID, gMenu));
9471 } while (result->NextRow());
9472
9473 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu IDs in {} ms", uint32(_gossipMenusStore.size()), GetMSTimeDiffToNow(oldMSTime));
9474}
9475
9477{
9478 uint32 oldMSTime = getMSTime();
9479
9480 _gossipMenuItemsStore.clear();
9481
9482 QueryResult result = WorldDatabase.Query(
9483 // 0 1 2 3 4 5 6 7 8 9 10 11 12
9484 "SELECT MenuID, OptionID, OptionIcon, OptionText, OptionBroadcastTextID, OptionType, OptionNpcFlag, ActionMenuID, ActionPoiID, BoxCoded, BoxMoney, BoxText, BoxBroadcastTextID "
9485 "FROM gossip_menu_option ORDER BY MenuID, OptionID");
9486
9487 if (!result)
9488 {
9489 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
9490 return;
9491 }
9492
9493 do
9494 {
9495 Field* fields = result->Fetch();
9496
9497 GossipMenuItems gMenuItem;
9498
9499 gMenuItem.MenuID = fields[0].GetUInt16();
9500 gMenuItem.OptionID = fields[1].GetUInt16();
9501 gMenuItem.OptionIcon = GossipOptionIcon(fields[2].GetUInt32());
9502 gMenuItem.OptionText = fields[3].GetString();
9503 gMenuItem.OptionBroadcastTextID = fields[4].GetUInt32();
9504 gMenuItem.OptionType = fields[5].GetUInt8();
9505 gMenuItem.OptionNpcFlag = fields[6].GetUInt32();
9506 gMenuItem.ActionMenuID = fields[7].GetUInt32();
9507 gMenuItem.ActionPoiID = fields[8].GetUInt32();
9508 gMenuItem.BoxCoded = fields[9].GetBool();
9509 gMenuItem.BoxMoney = fields[10].GetUInt32();
9510 gMenuItem.BoxText = fields[11].GetString();
9511 gMenuItem.BoxBroadcastTextID = fields[12].GetUInt32();
9512
9513 if (gMenuItem.OptionIcon >= GOSSIP_ICON_MAX)
9514 {
9515 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has unknown icon id {}. Replacing with GOSSIP_ICON_CHAT", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.OptionIcon);
9516 gMenuItem.OptionIcon = GOSSIP_ICON_CHAT;
9517 }
9518
9519 if (gMenuItem.OptionBroadcastTextID)
9520 {
9521 if (!GetBroadcastText(gMenuItem.OptionBroadcastTextID))
9522 {
9523 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has non-existing or incompatible OptionBroadcastTextID {}, ignoring.", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.OptionBroadcastTextID);
9524 gMenuItem.OptionBroadcastTextID = 0;
9525 }
9526 }
9527
9528 if (gMenuItem.OptionType >= GOSSIP_OPTION_MAX)
9529 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has unknown option id {}. Option will not be used", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.OptionType);
9530
9531 if (gMenuItem.ActionPoiID && !GetPointOfInterest(gMenuItem.ActionPoiID))
9532 {
9533 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing ActionPoiID {}, ignoring", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.ActionPoiID);
9534 gMenuItem.ActionPoiID = 0;
9535 }
9536
9537 if (gMenuItem.BoxBroadcastTextID)
9538 {
9539 if (!GetBroadcastText(gMenuItem.BoxBroadcastTextID))
9540 {
9541 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} has non-existing or incompatible BoxBroadcastTextID {}, ignoring.", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.BoxBroadcastTextID);
9542 gMenuItem.BoxBroadcastTextID = 0;
9543 }
9544 }
9545
9546 _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuID, gMenuItem));
9547 } while (result->NextRow());
9548
9549 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option entries in {} ms", uint32(_gossipMenuItemsStore.size()), GetMSTimeDiffToNow(oldMSTime));
9550}
9551
9553{
9554 auto itr = _creatureDefaultTrainers.find(creatureId);
9555 if (itr != _creatureDefaultTrainers.end())
9556 return itr->second;
9557
9558 return nullptr;
9559}
9560
9561void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, bool persist /*= true*/)
9562{
9563 VendorItemData& vList = _cacheVendorItemStore[entry];
9564 vList.AddItem(item, maxcount, incrtime, extendedCost);
9565
9566 if (persist)
9567 {
9569
9570 stmt->setUInt32(0, entry);
9571 stmt->setUInt32(1, item);
9572 stmt->setUInt8(2, maxcount);
9573 stmt->setUInt32(3, incrtime);
9574 stmt->setUInt32(4, extendedCost);
9575
9576 WorldDatabase.Execute(stmt);
9577 }
9578}
9579
9580bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= true*/)
9581{
9582 CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
9583 if (iter == _cacheVendorItemStore.end())
9584 return false;
9585
9586 if (!iter->second.RemoveItem(item))
9587 return false;
9588
9589 if (persist)
9590 {
9592
9593 stmt->setUInt32(0, entry);
9594 stmt->setUInt32(1, item);
9595
9596 WorldDatabase.Execute(stmt);
9597 }
9598
9599 return true;
9600}
9601
9602bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
9603{
9604 CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
9605 if (!cInfo)
9606 {
9607 if (player)
9609 else
9610 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: {}), ignore", vendor_entry);
9611 return false;
9612 }
9613
9614 if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
9615 {
9616 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
9617 {
9618 if (player)
9620 else
9621 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: {}) without vendor flag, ignore", vendor_entry);
9622
9623 if (skip_vendors)
9624 skip_vendors->insert(vendor_entry);
9625 }
9626 return false;
9627 }
9628
9629 if (!sObjectMgr->GetItemTemplate(id))
9630 {
9631 if (player)
9633 else
9634 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: {}) have in item list non-existed item ({}), ignore", vendor_entry, id);
9635 return false;
9636 }
9637
9638 if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
9639 {
9640 if (player)
9642 else
9643 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: {}) with wrong ExtendedCost ({}) for vendor ({}), ignore", id, ExtendedCost, vendor_entry);
9644 return false;
9645 }
9646
9647 if (maxcount > 0 && incrtime == 0)
9648 {
9649 if (player)
9650 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
9651 else
9652 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount` ({}) for item {} of vendor (Entry: {}) but `incrtime`=0, ignore", maxcount, id, vendor_entry);
9653 return false;
9654 }
9655 else if (maxcount == 0 && incrtime > 0)
9656 {
9657 if (player)
9658 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
9659 else
9660 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has `maxcount`=0 for item {} of vendor (Entry: {}) but `incrtime`<>0, ignore", id, vendor_entry);
9661 return false;
9662 }
9663
9664 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
9665 if (!vItems)
9666 return true; // later checks for non-empty lists
9667
9668 if (vItems->FindItemCostPair(id, ExtendedCost))
9669 {
9670 if (player)
9672 else
9673 TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items {} (with extended cost {}) for vendor (Entry: {}), ignoring", id, ExtendedCost, vendor_entry);
9674 return false;
9675 }
9676
9677 return true;
9678}
9679
9681{
9682 uint32 oldMSTime = getMSTime();
9683
9684 // We insert an empty placeholder here so we can use the
9685 // script id 0 as dummy for "no script found".
9686 _scriptNamesStore.emplace_back("");
9687
9688 QueryResult result = WorldDatabase.Query(
9689 "SELECT DISTINCT(ScriptName) FROM achievement_criteria_data WHERE ScriptName <> '' AND type = 11 "
9690 "UNION "
9691 "SELECT DISTINCT(ScriptName) FROM battlefield_template WHERE ScriptName <> '' "
9692 "UNION "
9693 "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' "
9694 "UNION "
9695 "SELECT DISTINCT(ScriptName) FROM creature WHERE ScriptName <> '' "
9696 "UNION "
9697 "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
9698 "UNION "
9699 "SELECT DISTINCT(ScriptName) FROM gameobject WHERE ScriptName <> '' "
9700 "UNION "
9701 "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
9702 "UNION "
9703 "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' "
9704 "UNION "
9705 "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
9706 "UNION "
9707 "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
9708 "UNION "
9709 "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
9710 "UNION "
9711 "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' "
9712 "UNION "
9713 "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' "
9714 "UNION "
9715 "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' "
9716 "UNION "
9717 "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
9718
9719 if (!result)
9720 {
9721 TC_LOG_INFO("server.loading", ">> Loaded empty set of Script Names!");
9722 return;
9723 }
9724
9725 _scriptNamesStore.reserve(result->GetRowCount() + 1);
9726
9727 do
9728 {
9729 _scriptNamesStore.push_back((*result)[0].GetString());
9730 }
9731 while (result->NextRow());
9732
9733 std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
9734
9735 TC_LOG_INFO("server.loading", ">> Loaded {} ScriptNames in {} ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
9736}
9737
9739{
9740 return _scriptNamesStore;
9741}
9742
9743std::string const& ObjectMgr::GetScriptName(uint32 id) const
9744{
9745 static std::string const empty = "";
9746 return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
9747}
9748
9749uint32 ObjectMgr::GetScriptId(std::string const& name)
9750{
9751 // use binary search to find the script name in the sorted vector
9752 // assume "" is the first element
9753 if (name.empty())
9754 return 0;
9755
9756 ScriptNameContainer::const_iterator itr = std::lower_bound(_scriptNamesStore.begin(), _scriptNamesStore.end(), name);
9757 if (itr == _scriptNamesStore.end() || (*itr != name))
9758 return 0;
9759
9760 return uint32(itr - _scriptNamesStore.begin());
9761}
9762
9764{
9765 uint32 oldMSTime = getMSTime();
9766
9767 _broadcastTextStore.clear(); // for reload case
9768
9769 // 0 1 2 3 4 5 6 7 8 9 10 11 12
9770 QueryResult result = WorldDatabase.Query("SELECT ID, LanguageID, `Text`, Text1, EmoteID1, EmoteID2, EmoteID3, EmoteDelay1, EmoteDelay2, EmoteDelay3, SoundEntriesID, EmotesID, Flags FROM broadcast_text");
9771 if (!result)
9772 {
9773 TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast texts. DB table `broadcast_text` is empty.");
9774 return;
9775 }
9776
9777 _broadcastTextStore.reserve(result->GetRowCount());
9778
9779 do
9780 {
9781 Field* fields = result->Fetch();
9782
9783 uint32 id = fields[0].GetUInt32();
9784 BroadcastTextEntry& bct = _broadcastTextStore.emplace(id, id).first->second;
9785
9786 bct.LanguageID = fields[1].GetUInt32();
9787 bct.Text[DEFAULT_LOCALE] = fields[2].GetStringView();
9788 bct.Text1[DEFAULT_LOCALE] = fields[3].GetStringView();
9789 bct.EmoteID[0] = fields[4].GetUInt32();
9790 bct.EmoteID[1] = fields[5].GetUInt32();
9791 bct.EmoteID[2] = fields[6].GetUInt32();
9792 bct.EmoteDelay[0] = fields[7].GetUInt32();
9793 bct.EmoteDelay[1] = fields[8].GetUInt32();
9794 bct.EmoteDelay[2] = fields[9].GetUInt32();
9795 bct.SoundEntriesID = fields[10].GetUInt32();
9796 bct.EmotesID = fields[11].GetUInt32();
9797 bct.Flags = fields[12].GetUInt32();
9798
9799 if (bct.SoundEntriesID)
9800 {
9801 if (!sSoundEntriesStore.LookupEntry(bct.SoundEntriesID))
9802 {
9803 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` has SoundEntriesID {} but sound does not exist.", bct.Id, bct.SoundEntriesID);
9804 bct.SoundEntriesID = 0;
9805 }
9806 }
9807
9809 {
9810 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` using LanguageID {} but Language does not exist.", bct.Id, bct.LanguageID);
9812 }
9813
9814 for (std::size_t i = 0; i < bct.EmoteID.size(); ++i)
9815 {
9816 if (bct.EmoteID[i] &&!sEmotesStore.LookupEntry(bct.EmoteID[i]))
9817 {
9818 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` has EmoteId{} {} but emote does not exist.", bct.Id, i + 1, bct.EmoteID[i]);
9819 bct.EmoteID[i] = 0;
9820 }
9821 }
9822 } while (result->NextRow());
9823
9824 TC_LOG_INFO("server.loading", ">> Loaded {} broadcast texts in {} ms", _broadcastTextStore.size(), GetMSTimeDiffToNow(oldMSTime));
9825}
9826
9828{
9829 uint32 oldMSTime = getMSTime();
9830
9831 // 0 1 2 3
9832 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text`, Text1 FROM broadcast_text_locale");
9833 if (!result)
9834 {
9835 TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast text locales. DB table `broadcast_text_locale` is empty.");
9836 return;
9837 }
9838
9839 do
9840 {
9841 Field* fields = result->Fetch();
9842
9843 uint32 id = fields[0].GetUInt32();
9844 std::string localeName = fields[1].GetString();
9845
9846 LocaleConstant locale = GetLocaleByName(localeName);
9847 if (locale == LOCALE_enUS)
9848 continue;
9849
9850 BroadcastTextContainer::iterator bct = _broadcastTextStore.find(id);
9851 if (bct == _broadcastTextStore.end())
9852 {
9853 TC_LOG_ERROR("sql.sql", "BroadcastText (Id: {}) in table `broadcast_text_locale` does not exist. Skipped!", id);
9854 continue;
9855 }
9856
9857 AddLocaleString(fields[2].GetString(), locale, bct->second.Text);
9858 AddLocaleString(fields[3].GetString(), locale, bct->second.Text1);
9859 } while (result->NextRow());
9860
9861 TC_LOG_INFO("server.loading", ">> Loaded {} broadcast text locales in {} ms", uint32(_broadcastTextStore.size()), GetMSTimeDiffToNow(oldMSTime));
9862}
9863
9865{
9866 CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
9867
9868 if (it != _creatureBaseStatsStore.end())
9869 return &(it->second);
9870
9871 struct DefaultCreatureBaseStats : public CreatureBaseStats
9872 {
9873 DefaultCreatureBaseStats()
9874 {
9875 BaseArmor = 1;
9876 for (uint8 j = 0; j < MAX_EXPANSIONS; ++j)
9877 {
9878 BaseHealth[j] = 1;
9879 BaseDamage[j] = 0.0f;
9880 }
9881 BaseMana = 0;
9882 AttackPower = 0;
9883 RangedAttackPower = 0;
9884 }
9885 };
9886 static const DefaultCreatureBaseStats defStats;
9887 return &defStats;
9888}
9889
9891{
9892 uint32 oldMSTime = getMSTime();
9893
9894 QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2 FROM creature_classlevelstats");
9895
9896 if (!result)
9897 {
9898 TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
9899 return;
9900 }
9901
9902 uint32 count = 0;
9903 do
9904 {
9905 Field* fields = result->Fetch();
9906
9907 uint8 Level = fields[0].GetUInt8();
9908 uint8 Class = fields[1].GetUInt8();
9909
9910 if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
9911 TC_LOG_ERROR("sql.sql", "Creature base stats for level {} has invalid class {}", Level, Class);
9912
9913 CreatureBaseStats stats;
9914
9915 for (uint8 i = 0; i < MAX_EXPANSIONS; ++i)
9916 {
9917 stats.BaseHealth[i] = fields[2 + i].GetUInt32();
9918
9919 if (stats.BaseHealth[i] == 0)
9920 {
9921 TC_LOG_ERROR("sql.sql", "Creature base stats for class {}, level {} has invalid zero base HP[{}] - set to 1", Class, Level, i);
9922 stats.BaseHealth[i] = 1;
9923 }
9924
9925 stats.BaseDamage[i] = fields[9 + i].GetFloat();
9926 if (stats.BaseDamage[i] < 0.0f)
9927 {
9928 TC_LOG_ERROR("sql.sql", "Creature base stats for class {}, level {} has invalid negative base damage[{}] - set to 0.0", Class, Level, i);
9929 stats.BaseDamage[i] = 0.0f;
9930 }
9931 }
9932
9933 stats.BaseMana = fields[5].GetUInt32();
9934 stats.BaseArmor = fields[6].GetUInt32();
9935
9936 stats.AttackPower = fields[7].GetUInt16();
9937 stats.RangedAttackPower = fields[8].GetUInt16();
9938
9939 _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
9940
9941 ++count;
9942 }
9943 while (result->NextRow());
9944
9945 for (auto const& creatureTemplatePair : _creatureTemplateStore)
9946 {
9947 for (uint16 lvl = creatureTemplatePair.second.minlevel; lvl <= creatureTemplatePair.second.maxlevel; ++lvl)
9948 {
9949 if (!_creatureBaseStatsStore.count(MAKE_PAIR16(lvl, creatureTemplatePair.second.unit_class)))
9950 TC_LOG_ERROR("sql.sql", "Missing base stats for creature class {} level {}", creatureTemplatePair.second.unit_class, lvl);
9951 }
9952 }
9953
9954 TC_LOG_INFO("server.loading", ">> Loaded {} creature base stats in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9955}
9956
9958{
9959 uint32 oldMSTime = getMSTime();
9960
9961 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
9962
9963 if (!result)
9964 {
9965 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
9966 return;
9967 }
9968
9969 uint32 count = 0;
9970
9971 do
9972 {
9973 Field* fields = result->Fetch();
9974
9975 uint32 alliance = fields[0].GetUInt32();
9976 uint32 horde = fields[1].GetUInt32();
9977
9978 if (!sAchievementStore.LookupEntry(alliance))
9979 TC_LOG_ERROR("sql.sql", "Achievement {} (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
9980 else if (!sAchievementStore.LookupEntry(horde))
9981 TC_LOG_ERROR("sql.sql", "Achievement {} (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
9982 else
9983 FactionChangeAchievements[alliance] = horde;
9984
9985 ++count;
9986 }
9987 while (result->NextRow());
9988
9989 TC_LOG_INFO("server.loading", ">> Loaded {} faction change achievement pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9990}
9991
9993{
9994 uint32 oldMSTime = getMSTime();
9995
9996 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");
9997
9998 if (!result)
9999 {
10000 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty.");
10001 return;
10002 }
10003
10004 uint32 count = 0;
10005
10006 do
10007 {
10008 Field* fields = result->Fetch();
10009
10010 uint32 alliance = fields[0].GetUInt32();
10011 uint32 horde = fields[1].GetUInt32();
10012
10013 if (!GetItemTemplate(alliance))
10014 TC_LOG_ERROR("sql.sql", "Item {} (alliance_id) referenced in `player_factionchange_items` does not exist, pair skipped!", alliance);
10015 else if (!GetItemTemplate(horde))
10016 TC_LOG_ERROR("sql.sql", "Item {} (horde_id) referenced in `player_factionchange_items` does not exist, pair skipped!", horde);
10017 else
10018 FactionChangeItems[alliance] = horde;
10019
10020 ++count;
10021 }
10022 while (result->NextRow());
10023
10024 TC_LOG_INFO("server.loading", ">> Loaded {} faction change item pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10025}
10026
10028{
10029 uint32 oldMSTime = getMSTime();
10030
10031 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
10032
10033 if (!result)
10034 {
10035 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
10036 return;
10037 }
10038
10039 uint32 count = 0;
10040
10041 do
10042 {
10043 Field* fields = result->Fetch();
10044
10045 uint32 alliance = fields[0].GetUInt32();
10046 uint32 horde = fields[1].GetUInt32();
10047
10048 if (!sObjectMgr->GetQuestTemplate(alliance))
10049 TC_LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
10050 else if (!sObjectMgr->GetQuestTemplate(horde))
10051 TC_LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
10052 else
10053 FactionChangeQuests[alliance] = horde;
10054
10055 ++count;
10056 }
10057 while (result->NextRow());
10058
10059 TC_LOG_INFO("server.loading", ">> Loaded {} faction change quest pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10060}
10061
10063{
10064 uint32 oldMSTime = getMSTime();
10065
10066 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
10067
10068 if (!result)
10069 {
10070 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
10071 return;
10072 }
10073
10074 uint32 count = 0;
10075
10076 do
10077 {
10078 Field* fields = result->Fetch();
10079
10080 uint32 alliance = fields[0].GetUInt32();
10081 uint32 horde = fields[1].GetUInt32();
10082
10083 if (!sFactionStore.LookupEntry(alliance))
10084 TC_LOG_ERROR("sql.sql", "Reputation {} (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
10085 else if (!sFactionStore.LookupEntry(horde))
10086 TC_LOG_ERROR("sql.sql", "Reputation {} (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
10087 else
10088 FactionChangeReputation[alliance] = horde;
10089
10090 ++count;
10091 }
10092 while (result->NextRow());
10093
10094 TC_LOG_INFO("server.loading", ">> Loaded {} faction change reputation pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10095}
10096
10098{
10099 uint32 oldMSTime = getMSTime();
10100
10101 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
10102
10103 if (!result)
10104 {
10105 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
10106 return;
10107 }
10108
10109 uint32 count = 0;
10110
10111 do
10112 {
10113 Field* fields = result->Fetch();
10114
10115 uint32 alliance = fields[0].GetUInt32();
10116 uint32 horde = fields[1].GetUInt32();
10117
10118 if (!sSpellMgr->GetSpellInfo(alliance))
10119 TC_LOG_ERROR("sql.sql", "Spell {} (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
10120 else if (!sSpellMgr->GetSpellInfo(horde))
10121 TC_LOG_ERROR("sql.sql", "Spell {} (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
10122 else
10123 FactionChangeSpells[alliance] = horde;
10124
10125 ++count;
10126 }
10127 while (result->NextRow());
10128
10129 TC_LOG_INFO("server.loading", ">> Loaded {} faction change spell pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10130}
10131
10133{
10134 uint32 oldMSTime = getMSTime();
10135
10136 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
10137
10138 if (!result)
10139 {
10140 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
10141 return;
10142 }
10143
10144 uint32 count = 0;
10145
10146 do
10147 {
10148 Field* fields = result->Fetch();
10149
10150 uint32 alliance = fields[0].GetUInt32();
10151 uint32 horde = fields[1].GetUInt32();
10152
10153 if (!sCharTitlesStore.LookupEntry(alliance))
10154 TC_LOG_ERROR("sql.sql", "Title {} (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
10155 else if (!sCharTitlesStore.LookupEntry(horde))
10156 TC_LOG_ERROR("sql.sql", "Title {} (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
10157 else
10158 FactionChangeTitles[alliance] = horde;
10159
10160 ++count;
10161 }
10162 while (result->NextRow());
10163
10164 TC_LOG_INFO("server.loading", ">> Loaded {} faction change title pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10165}
10166
10168{
10169 return Trinity::Containers::MapGetValuePtr(_gameObjectTemplateStore, entry);
10170}
10171
10173{
10174 auto itr = _gameObjectTemplateAddonStore.find(entry);
10175 if (itr != _gameObjectTemplateAddonStore.end())
10176 return &itr->second;
10177
10178 return nullptr;
10179}
10180
10182{
10183 return Trinity::Containers::MapGetValuePtr(_gameObjectOverrideStore, spawnId);
10184}
10185
10187{
10188 return Trinity::Containers::MapGetValuePtr(_creatureTemplateStore, entry);
10189}
10190
10192{
10193 return Trinity::Containers::MapGetValuePtr(_questPOIStore, questId);
10194}
10195
10197{
10198 return Trinity::Containers::MapGetValuePtr(_vehicleTemplateStore, veh->GetCreatureEntry());
10199}
10200
10202{
10203 if (Creature* cre = veh->GetBase()->ToCreature())
10204 {
10205 // Give preference to GUID-based accessories
10206 VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetSpawnId());
10207 if (itr != _vehicleAccessoryStore.end())
10208 return &itr->second;
10209 }
10210
10211 // Otherwise return entry-based
10212 VehicleAccessoryTemplateContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
10213 if (itr != _vehicleTemplateAccessoryStore.end())
10214 return &itr->second;
10215 return nullptr;
10216}
10217
10219{
10220 auto itr = _dungeonEncounterStore.find(MAKE_PAIR32(mapId, difficulty));
10221 if (itr != _dungeonEncounterStore.end())
10222 return &itr->second;
10223 return nullptr;
10224}
10225
10227{
10228 if (race >= MAX_RACES)
10229 return nullptr;
10230 if (class_ >= MAX_CLASSES)
10231 return nullptr;
10232 auto const& info = _playerInfo[race][class_];
10233 if (!info)
10234 return nullptr;
10235 return info.get();
10236}
10237
10239{
10240 uint32 oldMSTime = getMSTime();
10241
10242 // 0 1 2
10243 QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
10244
10245 if (!result)
10246 {
10247 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty.");
10248 return;
10249 }
10250
10251 uint32 count = 0;
10252 do
10253 {
10254 Field* fields = result->Fetch();
10255
10256 uint32 entry = fields[0].GetUInt32();
10257 uint32 item = fields[1].GetUInt32();
10258 uint32 idx = fields[2].GetUInt32();
10259
10260 GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
10261 if (!goInfo)
10262 {
10263 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: {}, idx: {}), skipped", entry, idx);
10264 continue;
10265 };
10266
10267 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10268 if (!db2Data)
10269 {
10270 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: {}) in gameobject (entry: {}, idx: {}), skipped", item, entry, idx);
10271 continue;
10272 };
10273
10274 _gameObjectQuestItemStore[entry].push_back(item);
10275
10276 ++count;
10277 }
10278 while (result->NextRow());
10279
10280 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10281}
10282
10284{
10285 uint32 oldMSTime = getMSTime();
10286
10287 // 0 1 2
10288 QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
10289
10290 if (!result)
10291 {
10292 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
10293 return;
10294 }
10295
10296 uint32 count = 0;
10297 do
10298 {
10299 Field* fields = result->Fetch();
10300
10301 uint32 entry = fields[0].GetUInt32();
10302 uint32 item = fields[1].GetUInt32();
10303 uint32 idx = fields[2].GetUInt32();
10304
10305 CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
10306 if (!creatureInfo)
10307 {
10308 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, idx: {}), skipped", entry, idx);
10309 continue;
10310 };
10311
10312 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10313 if (!db2Data)
10314 {
10315 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, idx: {}), skipped", item, entry, idx);
10316 continue;
10317 };
10318
10319 _creatureQuestItemStore[entry].push_back(item);
10320
10321 ++count;
10322 }
10323 while (result->NextRow());
10324
10325 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10326}
10327
10329{
10330 uint32 oldMSTime = getMSTime();
10331
10332 // cache disabled
10333 if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
10334 {
10335 TC_LOG_INFO("server.loading", ">> Query data caching is disabled. Skipped initialization.");
10336 return;
10337 }
10338
10340
10341 // Initialize Query data for creatures
10342 if (mask & QUERY_DATA_CREATURES)
10343 for (auto& creatureTemplatePair : _creatureTemplateStore)
10344 pool.PostWork([creature = &creatureTemplatePair.second]() { creature->InitializeQueryData(); });
10345
10346 // Initialize Query Data for gameobjects
10347 if (mask & QUERY_DATA_GAMEOBJECTS)
10348 for (auto& gameObjectTemplatePair : _gameObjectTemplateStore)
10349 pool.PostWork([gobj = &gameObjectTemplatePair.second]() { gobj->InitializeQueryData(); });
10350
10351 // Initialize Query Data for items
10352 if (mask & QUERY_DATA_ITEMS)
10353 for (auto& itemTemplatePair : _itemTemplateStore)
10354 pool.PostWork([item = &itemTemplatePair.second]() { item->InitializeQueryData(); });
10355
10356 // Initialize Query Data for quests
10357 if (mask & QUERY_DATA_QUESTS)
10358 for (auto& questTemplatePair : _questTemplates)
10359 pool.PostWork([quest = questTemplatePair.second.get()]() { quest->InitializeQueryData(); });
10360
10361 // Initialize Quest POI data
10362 if (mask & QUERY_DATA_POIS)
10363 for (auto& poiWrapperPair : _questPOIStore)
10364 pool.PostWork([poi = &poiWrapperPair.second]() { poi->InitializeQueryData(); });
10365
10366 pool.Join();
10367
10368 TC_LOG_INFO("server.loading", ">> Initialized query cache data in {} ms", GetMSTimeDiffToNow(oldMSTime));
10369}
10370
10372{
10373 QueryDataBuffer = BuildQueryData();
10374}
10375
10377{
10378 ByteBuffer tempBuffer;
10379 tempBuffer << uint32(POIData.QuestID); // quest ID
10380 tempBuffer << uint32(POIData.QuestPOIBlobDataStats.size()); // POI count
10381
10382 for (QuestPOIBlobData const& questPOIBlobData : POIData.QuestPOIBlobDataStats)
10383 {
10384 tempBuffer << uint32(questPOIBlobData.BlobIndex); // POI index
10385 tempBuffer << int32(questPOIBlobData.ObjectiveIndex); // objective index
10386 tempBuffer << uint32(questPOIBlobData.MapID); // mapid
10387 tempBuffer << uint32(questPOIBlobData.WorldMapAreaID); // areaid
10388 tempBuffer << uint32(questPOIBlobData.Floor); // floorid
10389 tempBuffer << uint32(questPOIBlobData.Unk3); // unknown
10390 tempBuffer << uint32(questPOIBlobData.Unk4); // unknown
10391 tempBuffer << uint32(questPOIBlobData.QuestPOIBlobPointStats.size()); // POI points count
10392
10393 for (QuestPOIBlobPoint const& questPOIBlobPoint : questPOIBlobData.QuestPOIBlobPointStats)
10394 {
10395 tempBuffer << int32(questPOIBlobPoint.X); // POI point x
10396 tempBuffer << int32(questPOIBlobPoint.Y); // POI point y
10397 }
10398 }
10399
10400 return tempBuffer;
10401}
#define sArenaTeamMgr
#define MAX_BAG_SIZE
Definition Bag.h:22
@ CHAR_UPD_ITEM_OWNER
@ CHAR_DEL_EMPTY_EXPIRED_MAIL
@ CHAR_DEL_ITEM_INSTANCE
@ 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(const std::string &name)
Definition Common.cpp:33
LocaleConstant
Definition Common.h:48
@ TOTAL_LOCALES
Definition Common.h:59
@ LOCALE_enUS
Definition Common.h:49
#define DEFAULT_LOCALE
Definition Common.h:62
@ DAY
Definition Common.h:31
#define M_PI
Definition Common.h:71
#define sCreatureAIRegistry
@ CREATURE_FLAG_EXTRA_DB_ALLOWED
@ CREATURE_FLAG_EXTRA_DUNGEON_BOSS
@ CREATURE_FLAG_EXTRA_TRIGGER
@ CREATURE_FLAG_EXTRA_INSTANCE_BIND
CreatureFlightMovementType
CreatureChaseMovementType
CreatureGroundMovementType
static const uint8 MAX_KILL_CREDIT
CreatureRandomMovementType
static const uint32 MAX_CREATURE_SPELLS
Difficulty
Definition DBCEnums.h:279
#define MAX_DIFFICULTY
Definition DBCEnums.h:294
@ MAX_LEVEL
Definition DBCEnums.h:49
@ STRONG_MAX_LEVEL
Definition DBCEnums.h:53
DBCStorage< CharTitlesEntry > sCharTitlesStore(CharTitlesEntryfmt)
DBCStorage< VehicleSeatEntry > sVehicleSeatStore(VehicleSeatEntryfmt)
DBCStorage< FactionEntry > sFactionStore(FactionEntryfmt)
SkillRaceClassInfoEntry const * GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_)
DBCStorage< ItemSetEntry > sItemSetStore(ItemSetEntryfmt)
DBCStorage< WorldSafeLocsEntry > sWorldSafeLocsStore(WorldSafeLocsEntryfmt)
ResponseCodes ValidateName(std::wstring const &name, LocaleConstant locale)
TaxiPathSetBySource sTaxiPathSetBySource
DBCStorage< MailTemplateEntry > sMailTemplateStore(MailTemplateEntryfmt)
DBCStorage< VehicleEntry > sVehicleStore(VehicleEntryfmt)
DBCStorage< ItemLimitCategoryEntry > sItemLimitCategoryStore(ItemLimitCategoryEntryfmt)
char const * GetPetName(uint32 petfamily, uint32 dbclang)
TaxiMask sTaxiNodesMask
DBCStorage< CharStartOutfitEntry > sCharStartOutfitStore(CharStartOutfitEntryfmt)
DBCStorage< LockEntry > sLockStore(LockEntryfmt)
DBCStorage< SkillTiersEntry > sSkillTiersStore(SkillTiersfmt)
TaxiPathNodesByPath sTaxiPathNodesByPath
DBCStorage< ItemEntry > sItemStore(Itemfmt)
DBCStorage< CreatureSpellDataEntry > sCreatureSpellDataStore(CreatureSpellDatafmt)
DBCStorage< CreatureDisplayInfoEntry > sCreatureDisplayInfoStore(CreatureDisplayInfofmt)
DBCStorage< ItemBagFamilyEntry > sItemBagFamilyStore(ItemBagFamilyfmt)
DBCStorage< ChrRacesEntry > sChrRacesStore(ChrRacesEntryfmt)
uint32 GetTalentSpellCost(uint32 spellId)
DBCStorage< GameObjectDisplayInfoEntry > sGameObjectDisplayInfoStore(GameObjectDisplayInfofmt)
DBCStorage< AreaTriggerEntry > sAreaTriggerStore(AreaTriggerEntryfmt)
DBCStorage< HolidaysEntry > sHolidaysStore(Holidaysfmt)
DBCStorage< ItemRandomSuffixEntry > sItemRandomSuffixStore(ItemRandomSuffixfmt)
DBCStorage< SoundEntriesEntry > sSoundEntriesStore(SoundEntriesfmt)
DBCStorage< AchievementEntry > sAchievementStore(Achievementfmt)
DBCStorage< TotemCategoryEntry > sTotemCategoryStore(TotemCategoryEntryfmt)
DBCStorage< QuestSortEntry > sQuestSortStore(QuestSortEntryfmt)
DBCStorage< GameObjectArtKitEntry > sGameObjectArtKitStore(GameObjectArtKitfmt)
DBCStorage< GemPropertiesEntry > sGemPropertiesStore(GemPropertiesEntryfmt)
DBCStorage< ItemRandomPropertiesEntry > sItemRandomPropertiesStore(ItemRandomPropertiesfmt)
DBCStorage< DungeonEncounterEntry > sDungeonEncounterStore(DungeonEncounterfmt)
MapDifficultyEntry const * GetMapDifficultyData(uint32 mapId, Difficulty difficulty)
DBCStorage< SkillLineEntry > sSkillLineStore(SkillLinefmt)
DBCStorage< CreatureFamilyEntry > sCreatureFamilyStore(CreatureFamilyfmt)
DBCStorage< FactionTemplateEntry > sFactionTemplateStore(FactionTemplateEntryfmt)
DBCStorage< TaxiNodesEntry > sTaxiNodesStore(TaxiNodesEntryfmt)
DBCStorage< SpellFocusObjectEntry > sSpellFocusObjectStore(SpellFocusObjectfmt)
DBCStorage< MapEntry > sMapStore(MapEntryfmt)
CharStartOutfitEntry const * GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender)
DBCStorage< CreatureModelDataEntry > sCreatureModelDataStore(CreatureModelDatafmt)
DBCStorage< CreatureTypeEntry > sCreatureTypeStore(CreatureTypefmt)
DBCStorage< ItemExtendedCostEntry > sItemExtendedCostStore(ItemExtendedCostEntryfmt)
DBCStorage< AreaTableEntry > sAreaTableStore(AreaTableEntryfmt)
DBCStorage< ChrClassesEntry > sChrClassesStore(ChrClassesEntryfmt)
DBCStorage< CurrencyTypesEntry > sCurrencyTypesStore(CurrencyTypesfmt)
DBCStorage< EmotesEntry > sEmotesStore(EmotesEntryfmt)
std::map< uint32, TaxiPathBySourceAndDestination > TaxiPathSetForSource
#define MAX_ITEM_SET_ITEMS
#define MAX_OUTFIT_ITEMS
#define MAX_SKILL_STEP
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.
uint8_t uint8
Definition Define.h:135
int16_t int16
Definition Define.h:130
int8_t int8
Definition Define.h:131
int32_t int32
Definition Define.h:129
uint64_t uint64
Definition Define.h:132
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
uint16 flags
@ DISABLE_TYPE_SPELL
Definition DisableMgr.h:27
@ DISABLE_TYPE_QUEST
Definition DisableMgr.h:28
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define ABORT_MSG
Definition Errors.h:75
#define ABORT
Definition Errors.h:74
#define ASSERT_NOTNULL(pointer)
Definition Errors.h:84
#define ASSERT
Definition Errors.h:68
#define sGameObjectAIRegistry
GossipOptionIcon
Definition GossipDef.h:60
@ GOSSIP_ICON_CHAT
Definition GossipDef.h:61
@ GOSSIP_ICON_MAX
Definition GossipDef.h:82
@ GOSSIP_OPTION_MAX
Definition GossipDef.h:56
#define sGroupMgr
Definition GroupMgr.h:58
#define sGuildMgr
Definition GuildMgr.h:59
@ TO_BE_DECIDED
uint32 GetItemEnchantMod(int32 entry)
@ ITEM_CLASS_MISCELLANEOUS
@ ITEM_FLAG2_FACTION_HORDE
@ ITEM_FLAG2_FACTION_ALLIANCE
@ ITEM_SPELLTRIGGER_ON_USE
@ BAG_FAMILY_MASK_CURRENCY_TOKENS
@ ITEM_MOD_SPELL_HEALING_DONE
@ ITEM_MOD_SPELL_DAMAGE_DONE
#define MAX_ITEM_CLASS
#define MAX_INVTYPE
#define MAX_ITEM_PROTO_SOCKETS
#define MAX_ITEM_PROTO_DAMAGES
#define MAX_ITEM_PROTO_SPELLS
#define SOCKET_COLOR_ALL
#define MAX_BIND_TYPE
#define MAX_ITEM_SPELLTRIGGER
#define MAX_ITEM_PROTO_STATS
const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS]
#define MAX_ITEM_MOD
@ ITEM_FLAGS_CU_DURATION_REAL_TIME
@ INVTYPE_HOLDABLE
@ INVTYPE_RANGED
@ INVTYPE_THROWN
@ INVTYPE_RANGEDRIGHT
@ INVTYPE_WEAPON
@ INVTYPE_WEAPONMAINHAND
@ INVTYPE_WEAPONOFFHAND
@ INVTYPE_2HWEAPON
@ INVTYPE_NON_EQUIP
@ INVTYPE_SHIELD
#define sLFGMgr
Definition LFGMgr.h:492
@ 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__,...)
Definition Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
#define TC_LOG_FATAL(filterType__,...)
Definition Log.h:168
LootStore LootTemplates_Gameobject("gameobject_loot_template", "gameobject entry", true)
@ MAIL_CHECK_MASK_COD_PAYMENT
This mail is payable on delivery.
Definition Mail.h:51
@ MAIL_CHECK_MASK_RETURNED
This mail was returned. Do not allow returning mail back again.
Definition Mail.h:49
@ MAIL_CHECK_MASK_NOT_RETURNABLE
This mail cannot be returned even if mail was not returned before.
Definition Mail.h:57
std::vector< MailItemInfo > MailItemInfoVec
Definition Mail.h:164
@ MAIL_NORMAL
Definition Mail.h:38
#define sMapMgr
Definition MapManager.h:211
@ MAX_DB_MOTION_TYPE
@ IDLE_MOTION_TYPE
@ WAYPOINT_MOTION_TYPE
@ RANDOM_MOTION_TYPE
#define MAX_GOSSIP_TEXT_OPTIONS
Definition NPCHandler.h:39
#define MAX_GOSSIP_TEXT_EMOTES
Definition NPCHandler.h:27
@ PHASEMASK_NORMAL
uint32 MAKE_PAIR32(uint16 l, uint16 h)
#define DEFAULT_PLAYER_COMBAT_REACH
#define DEFAULT_VISIBILITY_DISTANCE
TempSummonType
@ TEMPSUMMON_MANUAL_DESPAWN
#define INTERACTION_DISTANCE
uint16 MAKE_PAIR16(uint8 l, uint8 h)
VisibilityDistanceType
@ TYPEID_GAMEOBJECT
Definition ObjectGuid.h:40
@ TYPEID_UNIT
Definition ObjectGuid.h:38
HighGuid
Definition ObjectGuid.h:63
LanguageDesc lang_description[LANGUAGES_COUNT]
void CheckAndFixGOChairHeightId(GameObjectTemplate const *goInfo, uint32 &dataN, uint32 N)
static LanguageType 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:75
std::string GetScriptCommandName(ScriptCommands command)
Definition ObjectMgr.cpp:87
bool isValidString(const std::wstring &wstr, uint32 strictMask, bool numericOrSpace, bool create=false)
LanguageDesc const * GetLanguageDescByID(uint32 lang)
ScriptMapMap sEventScripts
Definition ObjectMgr.cpp:60
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:63
ScriptMapMap sWaypointScripts
Definition ObjectMgr.cpp:61
LanguageType
@ LT_ANY
@ LT_EXTENDEN_LATIN
@ LT_EAST_ASIA
@ LT_BASIC_LATIN
@ LT_CYRILLIC
bool normalizePlayerName(std::string &name)
std::pair< GraveyardContainer::iterator, GraveyardContainer::iterator > GraveyardMapBoundsNonConst
Definition ObjectMgr.h:855
#define MAX_PLAYER_NAME
Definition ObjectMgr.h:870
std::pair< GraveyardContainer::const_iterator, GraveyardContainer::const_iterator > GraveyardMapBounds
Definition ObjectMgr.h:854
std::multimap< uint32, ScriptInfo > ScriptMap
Definition ObjectMgr.h:410
#define MAX_CHARTER_NAME
Definition ObjectMgr.h:873
SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const *rcEntry)
std::pair< SpellScriptsContainer::iterator, SpellScriptsContainer::iterator > SpellScriptsBounds
Definition ObjectMgr.h:413
ScriptMapMap * GetScriptsMapByType(ScriptsType type)
Definition ObjectMgr.cpp:75
ScriptCommands
Definition ObjectMgr.h:101
@ SCRIPT_COMMAND_EMOTE
Definition ObjectMgr.h:103
@ SCRIPT_COMMAND_CREATE_ITEM
Definition ObjectMgr.h:119
@ SCRIPT_COMMAND_DESPAWN_SELF
Definition ObjectMgr.h:120
@ SCRIPT_COMMAND_CLOSE_DOOR
Definition ObjectMgr.h:114
@ SCRIPT_COMMAND_CAST_SPELL
Definition ObjectMgr.h:117
@ SCRIPT_COMMAND_RESPAWN_GAMEOBJECT
Definition ObjectMgr.h:111
@ SCRIPT_COMMAND_QUEST_EXPLORED
Definition ObjectMgr.h:109
@ SCRIPT_COMMAND_ACTIVATE_OBJECT
Definition ObjectMgr.h:115
@ SCRIPT_COMMAND_TALK
Definition ObjectMgr.h:102
@ SCRIPT_COMMAND_OPEN_DOOR
Definition ObjectMgr.h:113
@ SCRIPT_COMMAND_EQUIP
Definition ObjectMgr.h:128
@ SCRIPT_COMMAND_FIELD_SET
Definition ObjectMgr.h:104
@ SCRIPT_COMMAND_PLAYMOVIE
Definition ObjectMgr.h:131
@ SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT
Definition ObjectMgr.h:123
@ SCRIPT_COMMAND_TELEPORT_TO
Definition ObjectMgr.h:108
@ SCRIPT_COMMAND_MOVE_TO
Definition ObjectMgr.h:105
@ SCRIPT_COMMAND_FLAG_SET
Definition ObjectMgr.h:106
@ SCRIPT_COMMAND_TEMP_SUMMON_CREATURE
Definition ObjectMgr.h:112
@ SCRIPT_COMMAND_MOVEMENT
Definition ObjectMgr.h:132
@ SCRIPT_COMMAND_KILL_CREDIT
Definition ObjectMgr.h:110
@ SCRIPT_COMMAND_KILL
Definition ObjectMgr.h:124
@ SCRIPT_COMMAND_LOAD_PATH
Definition ObjectMgr.h:122
@ SCRIPT_COMMAND_ORIENTATION
Definition ObjectMgr.h:127
@ SCRIPT_COMMAND_PLAY_SOUND
Definition ObjectMgr.h:118
@ SCRIPT_COMMAND_MODEL
Definition ObjectMgr.h:129
@ SCRIPT_COMMAND_CLOSE_GOSSIP
Definition ObjectMgr.h:130
@ SCRIPT_COMMAND_REMOVE_AURA
Definition ObjectMgr.h:116
@ SCRIPT_COMMAND_FLAG_REMOVE
Definition ObjectMgr.h:107
std::string GetScriptCommandName(ScriptCommands command)
Definition ObjectMgr.cpp:87
QueryDataGroup
Definition ObjectMgr.h:911
@ QUERY_DATA_CREATURES
Definition ObjectMgr.h:912
@ QUERY_DATA_QUESTS
Definition ObjectMgr.h:915
@ QUERY_DATA_GAMEOBJECTS
Definition ObjectMgr.h:913
@ QUERY_DATA_ITEMS
Definition ObjectMgr.h:914
@ QUERY_DATA_POIS
Definition ObjectMgr.h:916
LanguageDesc const * GetLanguageDescByID(uint32 lang)
std::unordered_map< uint32, CellObjectGuids > CellObjectGuidsMap
Definition ObjectMgr.h:515
#define SPAWNGROUP_MAP_UNSET
Definition ObjectMgr.h:876
TC_GAME_API ScriptMapMap sEventScripts
Definition ObjectMgr.cpp:60
#define sObjectMgr
Definition ObjectMgr.h:1721
TC_GAME_API ScriptMapMap sWaypointScripts
Definition ObjectMgr.cpp:61
@ CHAT_TYPE_WHISPER
Definition ObjectMgr.h:142
std::map< uint32, ScriptMap > ScriptMapMap
Definition ObjectMgr.h:411
SummonerType
Definition ObjectMgr.h:65
@ SUMMONER_TYPE_MAP
Definition ObjectMgr.h:68
@ SUMMONER_TYPE_CREATURE
Definition ObjectMgr.h:66
@ SUMMONER_TYPE_GAMEOBJECT
Definition ObjectMgr.h:67
std::string GetScriptsTableNameByType(ScriptsType type)
Definition ObjectMgr.cpp:63
EncounterCreditType
Definition ObjectMgr.h:889
@ ENCOUNTER_CREDIT_KILL_CREATURE
Definition ObjectMgr.h:890
@ ENCOUNTER_CREDIT_CAST_SPELL
Definition ObjectMgr.h:891
ScriptsType
Definition ObjectMgr.h:173
@ SCRIPTS_WAYPOINT
Definition ObjectMgr.h:177
@ SCRIPTS_EVENT
Definition ObjectMgr.h:176
std::multimap< uint32, uint32 > QuestRelations
Definition ObjectMgr.h:578
std::vector< std::unique_ptr< DungeonEncounter const > > DungeonEncounterList
Definition ObjectMgr.h:905
#define MAX_PET_NAME
Definition ObjectMgr.h:872
SkillRangeType
Definition ObjectMgr.h:860
@ SKILL_RANGE_MONO
Definition ObjectMgr.h:863
@ SKILL_RANGE_NONE
Definition ObjectMgr.h:865
@ SKILL_RANGE_LANGUAGE
Definition ObjectMgr.h:861
@ SKILL_RANGE_RANK
Definition ObjectMgr.h:864
@ SKILL_RANGE_LEVEL
Definition ObjectMgr.h:862
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define QUEST_ITEM_DROP_COUNT
Definition QuestDef.h:40
#define QUEST_REWARD_ITEM_COUNT
Definition QuestDef.h:42
#define QUEST_ITEM_OBJECTIVES_COUNT
Definition QuestDef.h:39
#define QUEST_REWARD_REPUTATIONS_COUNT
Definition QuestDef.h:43
#define QUEST_OBJECTIVES_COUNT
Definition QuestDef.h:38
@ QUEST_FLAGS_TRACKING
Definition QuestDef.h:142
@ QUEST_FLAGS_DAILY
Definition QuestDef.h:144
@ QUEST_FLAGS_WEEKLY
Definition QuestDef.h:147
#define QUEST_REWARD_CHOICES_COUNT
Definition QuestDef.h:41
QuestSpecialFlags
Definition QuestDef.h:157
@ QUEST_SPECIAL_FLAGS_COMPLETED_AT_START
Definition QuestDef.h:175
@ QUEST_SPECIAL_FLAGS_CAST
Definition QuestDef.h:165
@ QUEST_SPECIAL_FLAGS_TIMED
Definition QuestDef.h:173
@ QUEST_SPECIAL_FLAGS_DELIVER
Definition QuestDef.h:170
@ QUEST_SPECIAL_FLAGS_REPEATABLE
Definition QuestDef.h:160
@ QUEST_SPECIAL_FLAGS_KILL
Definition QuestDef.h:172
@ QUEST_SPECIAL_FLAGS_DB_ALLOWED
Definition QuestDef.h:168
@ QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT
Definition QuestDef.h:161
@ QUEST_SPECIAL_FLAGS_SPEAKTO
Definition QuestDef.h:171
@ QUEST_SPECIAL_FLAGS_MONTHLY
Definition QuestDef.h:164
@ QUEST_SPECIAL_FLAGS_PLAYER_KILL
Definition QuestDef.h:174
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
#define sScriptMgr
Definition ScriptMgr.h:1168
@ GAMEOBJECT_TYPE_CAMERA
@ GAMEOBJECT_TYPE_CAPTURE_POINT
@ GAMEOBJECT_TYPE_BUTTON
@ GAMEOBJECT_TYPE_SPELL_FOCUS
@ 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_RITUAL
#define MAX_REPUTATION_RANK
@ SPELL_SCHOOL_NORMAL
@ SPELL_SCHOOL_HOLY
@ MAX_SPELL_SCHOOL
#define LANGUAGES_COUNT
@ SPELL_EFFECT_SEND_EVENT
@ SPELL_EFFECT_QUEST_COMPLETE
@ LANG_TITAN
@ LANG_GNOMISH_BINARY
@ LANG_UNIVERSAL
@ LANG_GNOMISH
@ LANG_GOBLIN_BINARY
@ LANG_DEMONIC
@ LANG_DRAENEI
@ LANG_THALASSIAN
@ LANG_DRACONIC
@ LANG_TROLL
@ LANG_TAURAHE
@ LANG_KALIMAG
@ LANG_ZOMBIE
@ LANG_ADDON
@ LANG_DWARVISH
@ LANG_GUTTERSPEAK
@ LANG_COMMON
@ LANG_ORCISH
@ LANG_DARNASSIAN
@ SKILL_CATEGORY_ARMOR
@ SKILL_CATEGORY_LANGUAGES
@ GENDER_MALE
@ GENDER_NONE
#define MIN_REPUTATION_RANK
#define MAX_PET_DIET
CreatureFamily
@ CREATURE_FAMILY_NONE
@ CREATURE_FAMILY_HORSE_CUSTOM
ResponseCodes
@ RESPONSE_FAILURE
@ CHAR_NAME_TOO_SHORT
@ CHAR_NAME_THREE_CONSECUTIVE
@ CHAR_NAME_INVALID_CHARACTER
@ CHAR_NAME_TOO_LONG
@ CHAR_NAME_RESERVED
@ CHAR_NAME_MIXED_LANGUAGES
@ CHAR_NAME_PROFANE
@ UNIT_CLASS_WARRIOR
#define MAX_RACES
#define MAX_SHEATHETYPE
@ CREATURE_TYPE_HUMANOID
InvisibilityType
@ TOTAL_INVISIBILITY_TYPES
@ INVISIBILITY_GENERAL
@ ITEM_QUALITY_NORMAL
@ MAX_ITEM_QUALITY
@ MAX_DB_ALLOWED_QUEST_TYPES
@ CLASS_HUNTER
@ CLASS_DRUID
@ CLASS_SHAMAN
@ CLASS_PRIEST
@ CLASS_WARRIOR
@ CLASS_WARLOCK
@ CLASS_MAGE
@ CLASS_DEATH_KNIGHT
@ CLASS_PALADIN
@ CLASS_ROGUE
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
#define MAX_SKILL_TYPE
@ ALLIANCE
@ HORDE
@ CHAT_MSG_RAID_BOSS_WHISPER
#define MAX_TOTEM_SLOT
#define MAX_CLASSES
@ SHEATHETYPE_NONE
SummonSlot
Races
@ RACE_DRAENEI
@ RACE_BLOODELF
@ RACE_HUMAN
#define CLASSMASK_ALL_PLAYABLE
#define MAX_GO_STATE
@ STAT_SPIRIT
@ STAT_INTELLECT
@ MAX_STATS
@ STAT_AGILITY
@ STAT_STRENGTH
@ STAT_STAMINA
PetNameInvalidReason
@ PET_NAME_INVALID
@ PET_NAME_RESERVED
@ PET_NAME_SUCCESS
@ PET_NAME_MIXED_LANGUAGES
@ PET_NAME_TOO_SHORT
@ PET_NAME_TOO_LONG
@ PET_NAME_PROFANE
#define RACEMASK_ALL_PLAYABLE
#define CLASSMASK_ALL_CREATURES
@ EXPANSION_THE_BURNING_CRUSADE
@ MAX_EXPANSIONS
@ EXPANSION_WRATH_OF_THE_LICH_KING
GOState
@ GO_STATE_READY
@ SKILL_LANG_GNOMISH
@ SKILL_LANG_COMMON
@ SKILL_LANG_DRAENEI
@ SKILL_LANG_ORCISH
@ SKILL_LANG_TROLL
@ SKILL_LANG_GUTTERSPEAK
@ SKILL_LANG_OLD_TONGUE
@ SKILL_LANG_TAURAHE
@ SKILL_LANG_TITAN
@ SKILL_LANG_DARNASSIAN
@ SKILL_LANG_DEMON_TONGUE
@ SKILL_LANG_DWARVEN
@ SKILL_LANG_THALASSIAN
@ SKILL_RUNEFORGING
@ SKILL_LANG_DRACONIC
#define MAX_SPILLOVER_FACTIONS
SpawnGroupFlags
Definition SpawnData.h:47
@ SPAWNGROUP_FLAGS_ALL
Definition SpawnData.h:55
@ SPAWNGROUP_FLAG_MANUAL_SPAWN
Definition SpawnData.h:51
@ SPAWNGROUP_FLAG_COMPATIBILITY_MODE
Definition SpawnData.h:50
@ SPAWNGROUP_FLAG_SYSTEM
Definition SpawnData.h:49
@ LINKED_RESPAWN_CREATURE_TO_GO
Definition SpawnData.h:111
@ LINKED_RESPAWN_GO_TO_GO
Definition SpawnData.h:112
@ LINKED_RESPAWN_CREATURE_TO_CREATURE
Definition SpawnData.h:110
@ LINKED_RESPAWN_GO_TO_CREATURE
Definition SpawnData.h:113
SpawnObjectType
Definition SpawnData.h:30
@ SPELL_AURA_CONTROL_VEHICLE
#define sSpellMgr
Definition SpellMgr.h:738
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
@ UNIT_FLAG2_ALLOWED
@ UNIT_STAND_STATE_SIT_HIGH_CHAIR
Definition UnitDefines.h:40
@ UNIT_STAND_STATE_SIT_LOW_CHAIR
Definition UnitDefines.h:38
@ MAX_UNIT_STAND_STATE
Definition UnitDefines.h:45
#define BASE_ATTACK_TIME
Definition UnitDefines.h:27
#define MAX_DECLINED_NAME_CASES
@ UNIT_NPC_FLAG_VENDOR
@ UNIT_NPC_FLAG_GOSSIP
@ UNIT_NPC_FLAG_QUESTGIVER
@ UNIT_NPC_FLAG_SPELLCLICK
@ MAX_SHEATH_STATE
#define MAX_EQUIPMENT_ITEMS
Definition UnitDefines.h:29
AnimTier
Definition UnitDefines.h:85
@ UNIT_FLAG_ALLOWED
std::wstring GetMainPartOfName(std::wstring const &wname, uint32 declension)
Definition Util.cpp:484
bool WStrToUtf8(wchar_t const *wstr, size_t size, std::string &utf8str)
Definition Util.cpp:433
void wstrToLower(std::wstring &str)
Definition Util.cpp:480
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition Util.cpp:383
bool isCyrillicString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:216
bool isBasicLatinString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:200
bool isEastAsianString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:224
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:554
bool isExtendedLatinString(std::wstring_view wstr, bool numericOrSpace)
Definition Util.h:208
struct WcharToUpper wcharToUpper
std::vector< VehicleAccessory > VehicleAccessoryList
@ WORLD_SEL_WAYPOINT_DATA_ACTION
@ 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_DEL_NPC_VENDOR
@ WORLD_INS_NPC_VENDOR
@ WORLD_DEL_GRAVEYARD_ZONE
void PSendSysMessage(char const *fmt, Args &&... args)
Definition Chat.h:61
virtual void SendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition Chat.cpp:102
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool allowDuplicate)
static float _GetDamageMod(int32 Rank)
Class used to access individual fields of database query result.
Definition Field.h:92
uint8 GetUInt8() const
Definition Field.cpp:29
std::string GetString() const
Definition Field.cpp:125
int8 GetInt8() const
Definition Field.cpp:37
std::string_view GetStringView() const
Definition Field.cpp:137
int16 GetInt16() const
Definition Field.cpp:53
uint16 GetUInt16() const
Definition Field.cpp:45
float GetFloat() const
Definition Field.cpp:93
bool GetBool() const
Definition Field.h:100
uint32 GetUInt32() const
Definition Field.cpp:61
int32 GetInt32() const
Definition Field.cpp:69
bool LoadFromDB(ObjectGuid::LowType spawnId, Map *map, bool addToMap, bool=true)
bool HaveQuestLootFor(uint32 loot_id) const
Definition LootMgr.cpp:198
static bool IsValidMapCoord(uint32 mapid, float x, float y)
Definition MapManager.h:90
static bool IsValidMAP(uint32 mapid, bool startUp)
Definition Map.h:302
bool IsRemovalGrid(float x, float y) const
Definition Map.h:344
bool Instanceable() const
Definition Map.cpp:4221
bool IsGridLoaded(uint32 gridId) const
Definition Map.h:351
uint32 LowType
Definition ObjectGuid.h:142
uint32 GetEntry() const
Definition ObjectGuid.h:155
static TypeID GetTypeId(HighGuid high)
Definition ObjectGuid.h:191
void GetPlayerClassLevelInfo(uint32 class_, uint8 level, PlayerClassLevelInfo *info) const
CreatureMovementData const * GetCreatureMovementOverride(ObjectGuid::LowType spawnId) const
QuestOfferRewardLocaleContainer _questOfferRewardLocaleStore
Definition ObjectMgr.h:1696
QuestPOIWrapper const * GetQuestPOIWrapper(uint32 questId) const
AccessRequirement const * GetAccessRequirement(uint32 mapid, Difficulty difficulty) const
CellObjectGuidsMap const * GetMapObjectGuids(uint16 mapid, uint8 spawnMode)
static void ChooseCreatureFlags(CreatureTemplate const *cinfo, uint32 *npcflag, uint32 *unit_flags, uint32 *dynamicflags, CreatureData const *data=nullptr)
EquipmentInfo const * GetEquipmentInfo(uint32 entry, int8 &id) const
void AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, bool persist=true)
TempSummonDataContainer _tempSummonDataStore
Stores temp summon data grouped by summoner's entry, summoner's type and group id.
Definition ObjectMgr.h:1689
QuestRelations _creatureQuestInvolvedRelations
Definition ObjectMgr.h:1607
PetLevelInfo const * GetPetLevelInfo(uint32 creature_id, uint8 level) const
void LoadCreatureTemplateResistances()
VehicleAccessoryList const * GetVehicleAccessoryList(Vehicle *veh) const
std::set< uint32 > _difficultyEntries[MAX_DIFFICULTY - 1]
Definition ObjectMgr.h:1712
VehicleAccessoryContainer _vehicleAccessoryStore
Definition ObjectMgr.h:1625
uint32 GetXPForLevel(uint8 level) const
void LoadCreatureTemplate(Field *fields)
GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore
Definition ObjectMgr.h:1683
CreatureBaseStats const * GetCreatureBaseStats(uint8 level, uint8 unitClass)
void LoadCreatureTemplateSpells()
InstanceTemplate const * GetInstanceTemplate(uint32 mapId) const
TrinityString const * GetTrinityString(uint32 entry) const
Definition ObjectMgr.h:1428
void LoadReputationOnKill()
CellObjectGuids const * GetCellObjectGuids(uint16 mapid, uint8 spawnMode, uint32 cell_id)
void LoadEventScripts()
void LoadGameObjectTemplate()
CreatureAddon const * GetCreatureTemplateAddon(uint32 entry) const
void LoadFactionChangeSpells()
void LoadFactionChangeItems()
void LoadAreaTriggerTeleports()
void LoadNpcTextLocales()
ObjectGuid::LowType AddCreatureData(uint32 entry, uint32 map, Position const &pos, uint32 spawntimedelay=0)
void LoadPageTextLocales()
AreaTriggerTeleport const * GetGoBackTrigger(uint32 Map) const
HalfNameContainer _petHalfName1
Definition ObjectMgr.h:1662
GossipTextContainer _gossipTextStore
Definition ObjectMgr.h:1587
void LoadVehicleTemplate()
void LoadInstanceTemplate()
void LoadScriptNames()
void LoadItemSetNames()
uint32 GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team=false)
SpellScriptsContainer _spellScriptsStore
Definition ObjectMgr.h:1621
SpawnGroupDataContainer _spawnGroupDataStore
Definition ObjectMgr.h:1685
WorldSafeLocsEntry const * GetDefaultGraveyard(uint32 team) const
void LoadQuestGreetingLocales()
CreatureDataContainer _creatureDataStore
Definition ObjectMgr.h:1668
void AddGameobjectToGrid(ObjectGuid::LowType guid, GameObjectData const *data)
void LoadPageTexts()
void ValidateSpellScripts()
void LoadCreatureQuestEnders()
QuestRelations _goQuestRelations
Definition ObjectMgr.h:1604
void LoadGameobjectQuestEnders()
void LoadCreatureMovementOverrides()
void LoadCreatureLocales()
TavernAreaTriggerContainer _tavernAreaTriggerStore
Definition ObjectMgr.h:1585
void LoadCreatureClassLevelStats()
PlayerInfo const * GetPlayerInfo(uint32 race, uint32 class_) const
void LoadCreatureTemplates()
GossipText const * GetGossipText(uint32 Text_ID) const
GameObjectTemplateAddon const * GetGameObjectTemplateAddon(uint32 entry) const
PageTextLocaleContainer _pageTextLocaleStore
Definition ObjectMgr.h:1700
EquipmentInfoContainer _equipmentInfoStore
Definition ObjectMgr.h:1677
ObjectGuid::LowType _gameObjectSpawnId
Definition ObjectMgr.h:1571
void LoadTrainers()
AccessRequirementContainer _accessRequirementStore
Definition ObjectMgr.h:1591
void LoadQuestAreaTriggers()
QuestGreetingLocaleContainer _questGreetingLocaleStore
Definition ObjectMgr.h:1703
CreatureTemplateContainer _creatureTemplateStore
Definition ObjectMgr.h:1669
bool AddGameTele(GameTele &data)
std::vector< std::string > ScriptNameContainer
Definition ObjectMgr.h:955
static bool CheckDeclinedNames(const std::wstring &w_ownname, DeclinedName const &names)
void LoadQuestOfferRewardLocale()
void LoadFactionChangeAchievements()
void LoadGameObjectOverrides()
SpellScriptsBounds GetSpellScriptsBounds(uint32 spellId)
QuestRequestItemsLocaleContainer _questRequestItemsLocaleStore
Definition ObjectMgr.h:1697
void AddCreatureToGrid(ObjectGuid::LowType guid, CreatureData const *data)
ItemSetNameContainer _itemSetNameStore
Definition ObjectMgr.h:1665
QuestGreetingContainer _questGreetingStore
Definition ObjectMgr.h:1588
SpawnGroupTemplateData const * GetLegacySpawnGroup() const
Definition ObjectMgr.h:1260
void LoadFactionChangeReputations()
std::set< uint32 > _transportMaps
Definition ObjectMgr.h:1715
void LoadPetNumber()
std::unordered_map< ObjectGuid::LowType, CreatureMovementData > _creatureMovementOverrides
Definition ObjectMgr.h:1673
GraveyardContainer GraveyardStore
Definition ObjectMgr.h:1516
bool RemoveVendorItem(uint32 entry, uint32 item, bool persist=true)
DungeonEncounterList const * GetDungeonEncounterList(uint32 mapId, Difficulty difficulty) const
std::string const & GetScriptName(uint32 id) const
GameObjectTemplateContainer _gameObjectTemplateStore
Definition ObjectMgr.h:1682
void LoadReputationSpilloverTemplate()
void LoadExplorationBaseXP()
void LoadFishingBaseSkillLevel()
std::set< uint32 > _hasDifficultyEntries[MAX_DIFFICULTY - 1]
Definition ObjectMgr.h:1713
InstanceSpawnGroupContainer _instanceSpawnGroupStore
Definition ObjectMgr.h:1687
void LoadQuestRequestItemsLocale()
PlayerTotemModelMap _playerTotemModel
Definition ObjectMgr.h:1717
void LoadPetLevelInfo()
void GetPlayerLevelInfo(uint32 race, uint32 class_, uint8 level, PlayerLevelInfo *info) const
CreatureAddon const * GetCreatureAddon(ObjectGuid::LowType lowguid) const
void LoadLinkedRespawn()
void LoadEquipmentTemplates()
GameObjectData const * GetGameObjectData(ObjectGuid::LowType spawnId) const
Definition ObjectMgr.h:1353
void LoadSpawnGroups()
void LoadGossipMenu()
ItemTemplateContainer _itemTemplateStore
Definition ObjectMgr.h:1692
void LoadQuestStartersAndEnders()
GameObjectOverride const * GetGameObjectOverride(ObjectGuid::LowType spawnId) const
GameObjectDataContainer _gameObjectDataStore
Definition ObjectMgr.h:1680
void LoadSpawnGroupTemplates()
AreaTriggerTeleport const * GetAreaTrigger(uint32 trigger) const
void LoadGraveyardZones()
uint32 GeneratePetNumber()
uint32 GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
void LoadPlayerInfo()
WorldSafeLocsEntry const * GetClosestGraveyard(float x, float y, float z, uint32 MapId, uint32 team, WorldObject *conditionObject) const
void LoadGossipMenuItemsLocales()
void LoadQuests()
CreatureLocaleContainer _creatureLocaleStore
Definition ObjectMgr.h:1679
void CheckCreatureTemplate(CreatureTemplate const *cInfo)
std::atomic< uint32 > _hiPetNumber
Definition ObjectMgr.h:1568
void LoadCreatureDefaultTrainers()
bool IsReservedName(std::string_view name) const
GameTele const * GetGameTeleExactName(std::string_view name) const
void LoadGameTele()
int32 GetBaseReputationOf(FactionEntry const *factionEntry, uint8 race, uint8 playerClass) const
PointOfInterestLocaleContainer _pointOfInterestLocaleStore
Definition ObjectMgr.h:1702
InstanceTemplateContainer _instanceTemplateStore
Definition ObjectMgr.h:1630
bool DeleteGameTele(std::string_view name)
void LoadQuestLocales()
QuestRelations _goQuestInvolvedRelations
Definition ObjectMgr.h:1605
void LoadCreatureQuestItems()
void LoadNPCSpellClickSpells()
QuestAreaTriggerContainer _questAreaTriggerStore
Definition ObjectMgr.h:1584
bool IsTransportMap(uint32 mapId) const
Definition ObjectMgr.h:1552
std::atomic< uint32 > _mailId
Definition ObjectMgr.h:1567
static PetNameInvalidReason CheckPetName(std::string_view name, LocaleConstant locale)
uint32 GetModelForTotem(SummonSlot totemSlot, Races race) const
ObjectGuid::LowType _creatureSpawnId
Definition ObjectMgr.h:1570
AreaTriggerContainer _areaTriggerStore
Definition ObjectMgr.h:1589
ScriptNameContainer const & GetAllScriptNames() const
std::unique_ptr< PlayerClassInfo > _playerClassInfo[MAX_CLASSES]
Definition ObjectMgr.h:1645
uint32 LoadReferenceVendor(int32 vendor, int32 item, std::set< uint32 > *skip_vendors)
void LoadAccessRequirements()
MapObjectGuids _mapObjectGuidsStore
Definition ObjectMgr.h:1667
CreatureAddonContainer _creatureAddonStore
Definition ObjectMgr.h:1671
QuestGreeting const * GetQuestGreeting(ObjectGuid guid) const
void LoadItemSetNameLocales()
PetLevelInfoContainer _petInfoStore
Definition ObjectMgr.h:1643
void RemoveGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist=false)
VehicleAccessoryTemplateContainer _vehicleTemplateAccessoryStore
Definition ObjectMgr.h:1624
static ObjectMgr * instance()
ObjectGuid::LowType AddGameObjectData(uint32 entry, uint32 map, Position const &pos, QuaternionData const &rot, uint32 spawntimedelay=0)
void LoadVehicleTemplateAccessories()
void DeleteGameObjectData(ObjectGuid::LowType spawnId)
NpcTextLocaleContainer _npcTextLocaleStore
Definition ObjectMgr.h:1699
HalfNameContainer _petHalfName0
Definition ObjectMgr.h:1661
void LoadGameObjectLocales()
void LoadTempSummons()
void LoadQuestGreetings()
QuestContainer _questTemplates
Definition ObjectMgr.h:1577
void PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint32 itemId, int32 count)
PageText const * GetPageText(uint32 pageEntry)
void LoadCreatureTemplateAddons()
static ResponseCodes CheckPlayerName(std::string_view name, LocaleConstant locale, bool create=false)
void LoadItemTemplates()
std::map< HighGuid, std::unique_ptr< ObjectGuidGenerator > > _guidGenerators
Definition ObjectMgr.h:1576
ItemSetNameLocaleContainer _itemSetNameLocaleStore
Definition ObjectMgr.h:1694
ObjectGuidGenerator & GetGuidSequenceGenerator(HighGuid high)
uint32 GetBaseXP(uint8 level)
CreatureTemplateAddonContainer _creatureTemplateAddonStore
Definition ObjectMgr.h:1672
static bool IsValidCharterName(std::string_view name)
std::unordered_map< uint32, VehicleTemplate > _vehicleTemplateStore
Definition ObjectMgr.h:1623
void LoadPetNames()
GameObjectTemplate const * GetGameObjectTemplate(uint32 entry) const
void LoadBroadcastTextLocales()
uint32 GetScriptId(std::string const &name)
void LoadBroadcastTexts()
std::unique_ptr< PlayerInfo > _playerInfo[MAX_RACES][MAX_CLASSES]
Definition ObjectMgr.h:1649
GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore
Definition ObjectMgr.h:1701
BaseXPContainer _baseXPTable
Definition ObjectMgr.h:1655
void LoadQuestPOI()
void LoadGameObjectTemplateAddons()
void RemoveCreatureFromGrid(ObjectGuid::LowType guid, CreatureData const *data)
void LoadTavernAreaTriggers()
void LoadReputationRewardRate()
AreaTriggerScriptContainer _areaTriggerScriptStore
Definition ObjectMgr.h:1590
ObjectGuid::LowType GenerateGameObjectSpawnId()
void CheckCreatureMovement(char const *table, uint64 id, CreatureMovementData &creatureMovement)
Trainer::Trainer const * GetTrainer(uint32 creatureId) const
QuestRelations _creatureQuestRelations
Definition ObjectMgr.h:1606
QuestLocaleContainer _questLocaleStore
Definition ObjectMgr.h:1695
Quest const * GetQuestTemplate(uint32 quest_id) const
ObjectGuid::LowType GenerateCreatureSpawnId()
VehicleTemplate const * GetVehicleTemplate(Vehicle *veh) const
DungeonEncounterContainer _dungeonEncounterStore
Definition ObjectMgr.h:1592
GameObjectAddon const * GetGameObjectAddon(ObjectGuid::LowType lowguid) const
void LoadGameobjectQuestStarters()
void LoadScripts(ScriptsType type)
void LoadGossipText()
RepOnKillContainer _repOnKillStore
Definition ObjectMgr.h:1595
void LoadGameObjectForQuests()
void LoadFactionChangeTitles()
uint32 GetAreaTriggerScriptId(uint32 trigger_id) const
SpawnGroupLinkContainer _spawnGroupMapStore
Definition ObjectMgr.h:1686
uint64 _equipmentSetGuid
Definition ObjectMgr.h:1566
RepSpilloverTemplateContainer _repSpilloverTemplateStore
Definition ObjectMgr.h:1596
void LoadAreaTriggerScripts()
bool SetCreatureLinkedRespawn(ObjectGuid::LowType guid, ObjectGuid::LowType linkedGuid)
SpawnMetadata const * GetSpawnMetadata(SpawnObjectType type, ObjectGuid::LowType spawnId) const
Definition ObjectMgr.h:1307
void ReturnOrDeleteOldMails(bool serverUp)
void LoadGameObjectAddons()
SpawnGroupTemplateData const * GetDefaultSpawnGroup() const
Definition ObjectMgr.h:1259
GameObjectData & NewOrExistGameObjectData(ObjectGuid::LowType spawnId)
Definition ObjectMgr.h:1359
CreatureModelInfo const * GetCreatureModelInfo(uint32 modelId) const
void LoadItemLocales()
void LoadWaypointScripts()
CreatureModelContainer _creatureModelStore
Definition ObjectMgr.h:1670
LinkedRespawnContainer _linkedRespawnStore
Definition ObjectMgr.h:1678
uint64 GenerateEquipmentSetGuid()
uint32 _auctionId
Definition ObjectMgr.h:1565
void GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
void DeleteCreatureData(ObjectGuid::LowType spawnId)
bool IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 ptime, uint32 ExtendedCost, Player *player=nullptr, std::set< uint32 > *skip_vendors=nullptr, uint32 ORnpcflag=0) const
void LoadInstanceEncounters()
GameObjectLocaleContainer _gameObjectLocaleStore
Definition ObjectMgr.h:1681
GameObjectAddonContainer _gameObjectAddonStore
Definition ObjectMgr.h:1674
void LoadCreatureQuestStarters()
GameTele const * GetGameTele(uint32 id) const
Definition ObjectMgr.h:1459
void LoadInstanceSpawnGroups()
PlayerXPperLevel _playerXPperLevel
Definition ObjectMgr.h:1652
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:1332
PointOfInterestContainer _pointsOfInterestStore
Definition ObjectMgr.h:1600
static void AddLocaleString(std::string &&value, LocaleConstant localeConstant, std::vector< std::string > &data)
void LoadGameObjectQuestItems()
void LoadPointsOfInterest()
void LoadFactionChangeQuests()
CreatureModelInfo const * GetCreatureModelRandomGender(uint32 *displayID) const
RepRewardRateContainer _repRewardRateStore
Definition ObjectMgr.h:1594
uint32 GenerateAuctionID()
std::string GeneratePetName(uint32 entry)
void LoadPointOfInterestLocales()
VehicleSeatAddonContainer _vehicleSeatAddonStore
Definition ObjectMgr.h:1718
bool LoadTrinityStrings()
CreatureData & NewOrExistCreatureData(ObjectGuid::LowType spawnId)
Definition ObjectMgr.h:1338
void LoadPlayerTotemModels()
PageTextContainer _pageTextStore
Definition ObjectMgr.h:1629
void LoadGossipMenuItems()
uint32 GenerateMailID()
ItemLocaleContainer _itemLocaleStore
Definition ObjectMgr.h:1693
ExclusiveQuestGroups _exclusiveQuestGroups
Definition ObjectMgr.h:1609
void LoadVehicleAccessories()
void RemoveGameobjectFromGrid(ObjectGuid::LowType guid, GameObjectData const *data)
void LoadVendors()
void LoadCreatureModelInfo()
void LoadVehicleSeatAddon()
GraveyardData const * FindGraveyardData(uint32 id, uint32 zone) const
void SetHighestGuids()
void InitializeQueriesData(QueryDataGroup mask)
void LoadReservedPlayersNames()
AreaTriggerTeleport const * GetMapEntranceTrigger(uint32 Map) const
QuestPOIContainer _questPOIStore
Definition ObjectMgr.h:1602
void OnDeleteSpawnData(SpawnData const *data)
void LoadGameObjects()
SpellClickInfoContainer _spellClickInfoStore
Definition ObjectMgr.h:1619
void LoadCreatureAddons()
bool AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist=true)
GameObjectOverrideContainer _gameObjectOverrideStore
Definition ObjectMgr.h:1684
void LoadCreatures()
static uint32 ChooseDisplayId(CreatureTemplate const *cinfo, CreatureData const *data=nullptr)
void LoadQuestRelationsHelper(QuestRelations &map, std::string const &table)
ItemTemplate const * GetItemTemplate(uint32 entry) const
static Creature * ToCreature(Object *o)
Definition Object.h:189
ObjectGuid GetGUID() const
Definition Object.h:79
static Player * ToPlayer(Object *o)
Definition Object.h:183
WorldSession * GetSession() const
Definition Player.h:1705
void setUInt16(uint8 index, uint16 value)
void setUInt32(uint8 index, uint32 value)
void setFloat(uint8 index, float value)
void setUInt64(uint8 index, uint64 value)
void setUInt8(uint8 index, uint8 value)
void setString(uint8 index, std::string const &value)
std::array< uint32, QUEST_OBJECTIVES_COUNT > RequiredNpcOrGoCount
Definition QuestDef.h:309
int32 _prevQuestID
Definition QuestDef.h:406
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionValue
Definition QuestDef.h:315
uint32 _requiredSkillPoints
Definition QuestDef.h:413
uint32 GetRewTitle() const
Definition QuestDef.h:276
std::vector< uint32 > DependentBreadcrumbQuests
Definition QuestDef.h:342
int32 _breadcrumbForQuestId
Definition QuestDef.h:409
void LoadQuestDetails(Field *fields)
Definition QuestDef.cpp:123
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemCount
Definition QuestDef.h:311
bool HasSpecialFlag(QuestSpecialFlags flag) const
Definition QuestDef.h:218
uint32 _requiredFactionId2
Definition QuestDef.h:363
uint32 _sourceItemIdCount
Definition QuestDef.h:418
static bool IsTakingQuestEnabled(uint32 questId)
Definition QuestDef.cpp:232
std::array< uint32, QUEST_REWARD_CHOICES_COUNT > RewardChoiceItemId
Definition QuestDef.h:310
int32 _rewardSpell
Definition QuestDef.h:386
void LoadQuestRequestItems(Field *fields)
Definition QuestDef.cpp:140
uint32 _requiredMinRepFaction
Definition QuestDef.h:414
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemId
Definition QuestDef.h:312
uint32 _requiredSkillId
Definition QuestDef.h:412
int32 _requiredMinRepValue
Definition QuestDef.h:415
uint32 _flags
Definition QuestDef.h:367
std::array< uint32, QUEST_REWARD_ITEM_COUNT > RewardItemCount
Definition QuestDef.h:313
uint32 GetSrcItemCount() const
Definition QuestDef.h:259
void LoadQuestMailSender(Field *fields)
Definition QuestDef.cpp:199
int32 _requiredFactionValue2
Definition QuestDef.h:364
int32 _exclusiveGroup
Definition QuestDef.h:408
uint32 _requiredFactionId1
Definition QuestDef.h:361
uint32 _sourceItemId
Definition QuestDef.h:375
int32 _requiredMaxRepValue
Definition QuestDef.h:417
std::array< uint32, QUEST_ITEM_OBJECTIVES_COUNT > RequiredItemId
Definition QuestDef.h:304
uint32 GetQuestType() const
Definition QuestDef.h:229
uint32 _requiredMaxRepFaction
Definition QuestDef.h:416
std::vector< uint32 > DependentPreviousQuests
Definition QuestDef.h:341
uint32 _allowableRaces
Definition QuestDef.h:360
uint32 GetQuestId() const
Definition QuestDef.h:228
uint32 _rewardNextQuest
Definition QuestDef.h:373
void LoadQuestOfferReward(Field *fields)
Definition QuestDef.cpp:156
uint32 _rewardMailDelay
Definition QuestDef.h:411
uint32 _requiredPlayerKills
Definition QuestDef.h:369
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDropQuantity
Definition QuestDef.h:307
uint32 _sourceSpellID
Definition QuestDef.h:405
int32 GetBreadcrumbForQuestId() const
Definition QuestDef.h:252
uint32 _limitTime
Definition QuestDef.h:366
uint32 _rewardDisplaySpell
Definition QuestDef.h:385
uint32 _specialFlags
Definition QuestDef.h:420
void SetSpecialFlag(QuestSpecialFlags flag)
Definition QuestDef.h:219
std::array< int32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionOverride
Definition QuestDef.h:316
std::array< uint32, QUEST_REWARD_REPUTATIONS_COUNT > RewardFactionId
Definition QuestDef.h:314
uint32 GetSrcItemId() const
Definition QuestDef.h:258
uint32 _allowableClasses
Definition QuestDef.h:404
int32 _requiredFactionValue1
Definition QuestDef.h:362
uint32 _id
Definition QuestDef.h:354
uint32 _rewardMailSenderEntry
Definition QuestDef.h:419
std::array< uint32, QUEST_ITEM_OBJECTIVES_COUNT > RequiredItemCount
Definition QuestDef.h:305
std::array< uint32, QUEST_ITEM_DROP_COUNT > ItemDrop
Definition QuestDef.h:306
int32 _zoneOrSort
Definition QuestDef.h:356
uint32 _rewardTitleId
Definition QuestDef.h:368
std::array< int32, QUEST_OBJECTIVES_COUNT > RequiredNpcOrGo
Definition QuestDef.h:308
uint32 _rewardMailTemplateId
Definition QuestDef.h:410
uint32 _nextQuestID
Definition QuestDef.h:407
void LoadQuestTemplateAddon(Field *fields)
Definition QuestDef.cpp:175
static const int32 Reputation_Cap
SpellInfo const * GetFirstRankSpell() const
uint32 Id
Definition SpellInfo.h:289
bool IsRanked() const
int32 GetDuration() const
SpellInfo const * GetNextRankSpell() const
bool HasAura(AuraType aura) const
std::array< SpellEffectInfo, MAX_SPELL_EFFECTS > const & GetEffects() const
Definition SpellInfo.h:482
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:71
uint32 value_type
Definition DBCEnums.h:437
Unit * GetSummonerUnit() const
decltype(auto) PostWork(T &&work)
Definition ThreadPool.h:33
Definition Unit.h:783
TempSummon * ToTempSummon()
Definition Unit.h:1810
bool IsSummon() const
Definition Unit.h:896
bool IsInRaidWith(Unit const *unit) const
Definition Unit.cpp:11863
bool IsInPartyWith(Unit const *unit) const
Definition Unit.cpp:11844
Unit * GetBase() const
May be called from scripts.
Definition Vehicle.h:45
uint32 GetCreatureEntry() const
Definition Vehicle.h:47
std::string const & GetName() const
Definition Object.h:384
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2802
static void StopNow(uint8 exitcode)
Definition World.h:674
#define sWorld
Definition World.h:901
@ REALM_ZONE_TEST_SERVER
Definition World.h:521
@ REALM_ZONE_LATIN_AMERICA
Definition World.h:499
@ REALM_ZONE_SPANISH
Definition World.h:506
@ REALM_ZONE_OCEANIC
Definition World.h:498
@ REALM_ZONE_KOREA
Definition World.h:501
@ REALM_ZONE_UNKNOWN
Definition World.h:495
@ REALM_ZONE_UNITED_STATES
Definition World.h:497
@ REALM_ZONE_ENGLISH
Definition World.h:503
@ REALM_ZONE_FRENCH
Definition World.h:505
@ REALM_ZONE_QA_SERVER
Definition World.h:523
@ REALM_ZONE_CHINA
Definition World.h:511
@ REALM_ZONE_RUSSIAN
Definition World.h:507
@ REALM_ZONE_DEVELOPMENT
Definition World.h:496
@ REALM_ZONE_TAIWAN
Definition World.h:509
@ REALM_ZONE_GERMAN
Definition World.h:504
@ CONFIG_STRICT_PET_NAMES
Definition World.h:225
@ CONFIG_MIN_PET_NAME
Definition World.h:228
@ CONFIG_MAX_PLAYER_LEVEL
Definition World.h:237
@ CONFIG_MIN_CHARTER_NAME
Definition World.h:227
@ CONFIG_STRICT_CHARTER_NAMES
Definition World.h:224
@ CONFIG_REALM_ZONE
Definition World.h:222
@ CONFIG_STRICT_PLAYER_NAMES
Definition World.h:223
@ CONFIG_EXPANSION
Definition World.h:283
@ CONFIG_MIN_PLAYER_NAME
Definition World.h:226
@ CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA
Definition World.h:164
@ CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA
Definition World.h:163
@ CONFIG_CACHE_DATA_QUERIES
Definition World.h:176
@ CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES
Definition World.h:148
@ ERROR_EXIT_CODE
Definition World.h:64
bool IsDisabledFor(DisableType type, uint32 entry, WorldObject const *ref, uint8 flags)
time_t GetGameTime()
Definition GameTime.cpp:42
TC_GAME_API Player * FindConnectedPlayer(ObjectGuid const &)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:29
TC_COMMON_API std::vector< std::string_view > Tokenize(std::string_view str, char sep, bool keepEmpty)
Definition Util.cpp:56
bool IsValidMapCoord(float c)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
CellCoord ComputeCellCoord(float x, float y)
std::array< uint32, 3 > EmoteDelay
Definition ObjectMgr.h:484
std::vector< std::string > Text
Definition ObjectMgr.h:481
std::array< uint32, 3 > EmoteID
Definition ObjectMgr.h:483
std::vector< std::string > Text1
Definition ObjectMgr.h:482
CellGuidSet gameobjects
Definition ObjectMgr.h:513
CellGuidSet creatures
Definition ObjectMgr.h:512
int32 ItemID[MAX_OUTFIT_ITEMS]
uint32 FemaleDisplayID
uint32 MaleDisplayID
uint32 GetId() const
std::vector< uint32 > auras
VisibilityDistanceType visibilityDistanceType
float BaseDamage[MAX_EXPANSIONS]
uint32 BaseHealth[MAX_EXPANSIONS]
uint32 GenerateMana(CreatureTemplate const *info) const
uint32 GenerateHealth(CreatureTemplate const *info) const
float wander_distance
uint32 dynamicflags
uint32 unit_flags
uint32 currentwaypoint
std::vector< std::string > Title
std::vector< std::string > Name
uint32 displayId_other_gender
CreatureRandomMovementType Random
CreatureFlightMovementType Flight
CreatureChaseMovementType Chase
CreatureGroundMovementType Ground
CreatureFamily family
uint32 MechanicImmuneMask
std::string Name
uint32 spells[MAX_CREATURE_SPELLS]
int32 resistance[MAX_SPELL_SCHOOL]
std::string StringId
uint32 SpellSchoolImmuneMask
CreatureMovementData Movement
uint32 KillCredit[MAX_KILL_CREDIT]
uint32 GetFirstInvisibleModel() const
Definition Creature.cpp:132
std::string Title
uint32 DifficultyEntry[MAX_DIFFICULTY - 1]
std::string AIName
uint32 GetRandomValidModelId() const
Definition Creature.cpp:110
std::string IconName
std::string name[MAX_DECLINED_NAME_CASES]
char const * Name[16]
uint32 ItemEntry[MAX_EQUIPMENT_ITEMS]
int32 ReputationBase[4]
uint32 ReputationClassMask[4]
uint32 ParentFactionID
uint32 ReputationRaceMask[4]
bool CanHaveReputation() const
QuaternionData ParentRotation
InvisibilityType invisibilityType
QuaternionData rotation
std::vector< std::string > Name
std::vector< std::string > CastBarCaption
std::array< uint32, 4 > artKits
struct GameObjectTemplate::@191::@214 flagdrop
struct GameObjectTemplate::@191::@210 spellcaster
struct GameObjectTemplate::@191::@199 chair
struct GameObjectTemplate::@191::@200 spellFocus
uint32 data[MAX_GAMEOBJECT_DATA]
struct GameObjectTemplate::@191::@213 fishinghole
struct GameObjectTemplate::@191::@204 areadamage
bool IsDespawnAtAction() const
struct GameObjectTemplate::@191::@206 moTransport
struct GameObjectTemplate::@191::@223 raw
struct GameObjectTemplate::@191::@212 flagstand
struct GameObjectTemplate::@191::@219 barberChair
struct GameObjectTemplate::@191::@202 goober
struct GameObjectTemplate::@191::@205 camera
struct GameObjectTemplate::@191::@196 chest
struct GameObjectTemplate::@191::@195 questgiver
struct GameObjectTemplate::@191::@193 door
std::string castBarCaption
struct GameObjectTemplate::@191::@194 button
struct GameObjectTemplate::@191::@198 trap
float position_y
Definition ObjectMgr.h:162
float orientation
Definition ObjectMgr.h:164
float position_x
Definition ObjectMgr.h:161
uint32 mapId
Definition ObjectMgr.h:165
std::string name
Definition ObjectMgr.h:166
float position_z
Definition ObjectMgr.h:163
std::wstring wnameLow
Definition ObjectMgr.h:167
std::vector< std::string > BoxText
Definition ObjectMgr.h:563
std::vector< std::string > OptionText
Definition ObjectMgr.h:562
uint32 BoxBroadcastTextID
Definition ObjectMgr.h:779
std::string OptionText
Definition ObjectMgr.h:770
uint32 ActionMenuID
Definition ObjectMgr.h:774
uint32 OptionNpcFlag
Definition ObjectMgr.h:773
uint32 ActionPoiID
Definition ObjectMgr.h:775
std::string BoxText
Definition ObjectMgr.h:778
GossipOptionIcon OptionIcon
Definition ObjectMgr.h:769
uint32 OptionType
Definition ObjectMgr.h:772
uint32 OptionBroadcastTextID
Definition ObjectMgr.h:771
uint32 TextID
Definition ObjectMgr.h:786
uint32 MenuID
Definition ObjectMgr.h:785
std::string Text_0
Definition NPCHandler.h:31
QEmote Emotes[MAX_GOSSIP_TEXT_EMOTES]
Definition NPCHandler.h:36
uint32 BroadcastTextID
Definition NPCHandler.h:33
std::string Text_1
Definition NPCHandler.h:32
GossipTextOption Options[MAX_GOSSIP_TEXT_OPTIONS]
Definition NPCHandler.h:43
uint32 safeLocId
Definition ObjectMgr.h:849
int32 SoundOverrideSubclassID
uint32 SheatheType
uint32 InventoryType
uint32 DisplayInfoID
uint32 ClassID
int32 Material
std::vector< std::string > Description
std::vector< std::string > Name
uint32 ItemID[MAX_ITEM_SET_ITEMS]
std::string name
std::vector< std::string > Name
int32 RandomProperty
std::array< uint32, MAX_ITEM_PROTO_FLAGS > Flags
std::array< ItemEffect, MAX_ITEM_PROTO_SPELLS > Effects
uint32 DisenchantID
uint32 RequiredCityRank
uint32 RequiredSkill
uint32 RequiredSpell
uint32 ScalingStatValue
std::string Description
uint32 AllowableClass
uint32 RequiredSkillRank
uint32 RequiredHonorRank
float RangedModRange
float ArmorDamageModifier
std::array< _Socket, MAX_ITEM_PROTO_SOCKETS > Socket
uint32 socketBonus
uint32 RequiredDisenchantSkill
uint32 TotemCategory
uint32 MinMoneyLoot
uint32 MaxMoneyLoot
uint32 PageMaterial
uint32 RequiredLevel
std::string Name1
uint32 GemProperties
uint32 RequiredReputationRank
uint32 ContainerSlots
uint32 DisplayInfoID
uint32 AllowableRace
uint32 RequiredReputationFaction
bool HasFlag(ItemFlags flag) const
uint32 ItemLimitCategory
uint32 ScalingStatDistribution
uint32 MaxDurability
uint32 InventoryType
uint32 LanguageID
std::array< _ItemStat, MAX_ITEM_PROTO_STATS > ItemStat
uint32 StartQuest
int32 SoundOverrideSubclass
std::array< _Damage, MAX_ITEM_PROTO_DAMAGES > Damage
uint32 StatsCount
ObjectGuid::LowType item_guid
Definition Mail.h:161
uint32 item_template
Definition Mail.h:162
Definition Mail.h:167
ObjectGuid::LowType receiver
Definition Mail.h:173
uint8 messageType
Definition Mail.h:169
uint32 messageID
Definition Mail.h:168
time_t expire_time
Definition Mail.h:178
ObjectGuid::LowType sender
Definition Mail.h:172
std::vector< MailItemInfo > items
Definition Mail.h:176
time_t deliver_time
Definition Mail.h:179
uint32 COD
Definition Mail.h:181
uint32 checked
Definition Mail.h:182
uint16 mailTemplateId
Definition Mail.h:171
int32 CorpseMapID
DBCPosition2D Corpse
bool IsBattlegroundOrArena() const
bool IsDungeon() const
bool Instanceable() const
std::vector< std::string > Text_0[MAX_GOSSIP_TEXT_OPTIONS]
Definition NPCHandler.h:55
std::vector< std::string > Text_1[MAX_GOSSIP_TEXT_OPTIONS]
Definition NPCHandler.h:56
std::vector< std::string > Text
Definition NPCHandler.h:48
std::string Text
Definition ObjectMgr.h:60
uint32 NextPageID
Definition ObjectMgr.h:61
uint16 health
Definition ObjectMgr.h:702
uint32 armor
Definition ObjectMgr.h:704
uint16 minDamage
Definition ObjectMgr.h:705
uint16 stats[MAX_STATS]
Definition ObjectMgr.h:701
uint16 maxDamage
Definition ObjectMgr.h:706
uint16 mana
Definition ObjectMgr.h:703
uint16 stats[MAX_STATS]
Definition ObjectMgr.h:651
std::vector< std::string > Name
Definition ObjectMgr.h:570
std::string Name
Definition ObjectMgr.h:762
uint32 Importance
Definition ObjectMgr.h:761
std::string ToString() const
Definition Position.cpp:149
float GetOrientation() const
Definition Position.h:82
float GetPositionX() const
Definition Position.h:79
float GetPositionY() const
Definition Position.h:80
void Relocate(float x, float y)
Definition Position.h:66
uint32 _Emote
Definition NPCHandler.h:23
uint32 _Delay
Definition NPCHandler.h:24
bool isUnit() const
static QuaternionData fromEulerAnglesZYX(float Z, float Y, float X)
std::vector< std::string > greeting
Definition ObjectMgr.h:525
std::array< std::vector< std::string >, QUEST_OBJECTIVES_COUNT > ObjectiveText
Definition QuestDef.h:185
std::vector< std::string > QuestCompletionLog
Definition QuestDef.h:184
std::vector< std::string > LogDescription
Definition QuestDef.h:181
std::vector< std::string > AreaDescription
Definition QuestDef.h:183
std::vector< std::string > LogTitle
Definition QuestDef.h:180
std::vector< std::string > QuestDescription
Definition QuestDef.h:182
std::vector< std::string > RewardText
Definition QuestDef.h:195
std::vector< QuestPOIBlobPoint > QuestPOIBlobPointStats
Definition ObjectMgr.h:812
uint32 WorldMapAreaID
Definition ObjectMgr.h:808
std::vector< QuestPOIBlobData > QuestPOIBlobDataStats
Definition ObjectMgr.h:818
uint32 QuestID
Definition ObjectMgr.h:817
QuestPOIData POIData
Definition ObjectMgr.h:823
ByteBuffer BuildQueryData() const
void InitializeQueryData()
QuestRelations::const_iterator _it
Definition ObjectMgr.h:609
QuestRelations::const_iterator _end
Definition ObjectMgr.h:609
bool HasQuest(uint32 questId) const
QuestRelations::const_iterator _end
Definition ObjectMgr.h:623
QuestRelations::const_iterator _begin
Definition ObjectMgr.h:623
std::vector< std::string > CompletionText
Definition QuestDef.h:190
float questMonthlyRate
Definition ObjectMgr.h:728
float questWeeklyRate
Definition ObjectMgr.h:727
float questDailyRate
Definition ObjectMgr.h:726
float questRepeatableRate
Definition ObjectMgr.h:729
float spellRate
Definition ObjectMgr.h:731
float questRate
Definition ObjectMgr.h:725
float creatureRate
Definition ObjectMgr.h:730
uint32 faction_rank[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:751
uint32 faction[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:749
float faction_rate[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:750
float Orientation
Definition ObjectMgr.h:276
uint32 Flags
Definition ObjectMgr.h:233
ScriptsType type
Definition ObjectMgr.h:217
uint32 QuestID
Definition ObjectMgr.h:281
int32 TextID
Definition ObjectMgr.h:234
struct ScriptInfo::@228::@242 RemoveAura
struct ScriptInfo::@228::@241 ToggleDoor
struct ScriptInfo::@228::@231 Talk
float DestX
Definition ObjectMgr.h:255
uint32 ItemEntry
Definition ObjectMgr.h:341
uint32 ChatType
Definition ObjectMgr.h:232
uint32 id
Definition ObjectMgr.h:218
uint32 delay
Definition ObjectMgr.h:219
float fData[4]
Definition ObjectMgr.h:227
struct ScriptInfo::@228::@237 QuestExplored
struct ScriptInfo::@228::@232 Emote
float PosY
Definition ObjectMgr.h:304
ScriptCommands command
Definition ObjectMgr.h:220
float DestY
Definition ObjectMgr.h:256
uint32 MapID
Definition ObjectMgr.h:269
uint32 nData[3]
Definition ObjectMgr.h:226
float PosZ
Definition ObjectMgr.h:305
struct ScriptInfo::@228::@236 TeleportTo
ObjectGuid::LowType GOGuid
Definition ObjectMgr.h:293
struct ScriptInfo::@228::@239 RespawnGameobject
float PosX
Definition ObjectMgr.h:303
struct ScriptInfo::@228::@243 CastSpell
struct ScriptInfo::@228::@230 Raw
uint32 Distance
Definition ObjectMgr.h:282
uint32 SpellID
Definition ObjectMgr.h:320
std::string GetDebugInfo() const
struct ScriptInfo::@228::@238 KillCredit
float DestZ
Definition ObjectMgr.h:257
struct ScriptInfo::@228::@245 CreateItem
struct ScriptInfo::@228::@240 TempSummonCreature
uint32 CreatureEntry
Definition ObjectMgr.h:287
uint32 Amount
Definition ObjectMgr.h:342
uint32 EmoteID
Definition ObjectMgr.h:239
uint32 scriptId
Definition SpawnData.h:101
uint32 phaseMask
Definition SpawnData.h:98
uint8 spawnMask
Definition SpawnData.h:100
uint32 id
Definition SpawnData.h:96
Position spawnPoint
Definition SpawnData.h:97
int32 spawntimesecs
Definition SpawnData.h:99
std::string StringId
Definition SpawnData.h:102
SpawnGroupFlags flags
Definition SpawnData.h:63
SpawnObjectType const type
Definition SpawnData.h:84
uint32 spawnId
Definition SpawnData.h:85
SpawnGroupTemplateData const * spawnGroupData
Definition SpawnData.h:88
static constexpr bool TypeIsValid(SpawnObjectType type)
Definition SpawnData.h:78
uint32 mapId
Definition SpawnData.h:86
bool IsFitToRequirements(Unit const *clicker, Unit const *clickee) const
SpellClickUserTypes userType
Definition ObjectMgr.h:442
DBCPosition3D Pos
uint32 MountCreatureID[2]
Stores data for temp summons.
Definition ObjectMgr.h:90
TempSummonType type
Summon type, see TempSummonType for available types.
Definition ObjectMgr.h:93
uint32 time
Despawn time, usable only with certain temp summon types.
Definition ObjectMgr.h:94
uint32 entry
Entry of summoned creature.
Definition ObjectMgr.h:91
Position pos
Position, where should be creature spawned.
Definition ObjectMgr.h:92
Key for storing temp summon data in TempSummonDataContainer.
Definition ObjectMgr.h:75
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:520
Milliseconds DespawnDelay
void AddItem(uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost)
VendorItem const * FindItemCostPair(uint32 item_id, uint32 extendedCost) const
Definition Creature.cpp:102
DBCPosition3D Loc