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, std::string(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, std::string(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->modelid_other_gender != 0 && urand(0, 1) == 0)
1699 {
1700 CreatureModelInfo const* minfo_tmp = GetCreatureModelInfo(modelInfo->modelid_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->modelid_other_gender);
1703 else
1704 {
1705 // Model ID changed
1706 *displayID = modelInfo->modelid_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, Gender, DisplayID_Other_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 modelId = fields[0].GetUInt32();
1734 CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(modelId);
1735 if (!creatureDisplay)
1736 {
1737 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has model for nonexistent display id ({}).", modelId);
1738 continue;
1739 }
1740
1741 CreatureModelInfo& modelInfo = _creatureModelStore[modelId];
1742
1743 modelInfo.bounding_radius = fields[1].GetFloat();
1744 modelInfo.combat_reach = fields[2].GetFloat();
1745 modelInfo.gender = fields[3].GetUInt8();
1746 modelInfo.modelid_other_gender = fields[4].GetUInt32();
1747 modelInfo.is_trigger = false;
1748
1749 // Checks
1750
1751 if (modelInfo.gender > GENDER_NONE)
1752 {
1753 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has wrong gender ({}) for display id ({}).", uint32(modelInfo.gender), modelId);
1754 modelInfo.gender = GENDER_MALE;
1755 }
1756
1757 if (modelInfo.modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.modelid_other_gender))
1758 {
1759 TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has nonexistent alt.gender model ({}) for existed display id ({}).", modelInfo.modelid_other_gender, modelId);
1760 modelInfo.modelid_other_gender = 0;
1761 }
1762
1763 if (modelInfo.combat_reach < 0.1f)
1765
1766 if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelID))
1767 modelInfo.is_trigger = strstr(modelData->ModelName, "InvisibleStalker") != nullptr;
1768
1769 ++count;
1770 }
1771 while (result->NextRow());
1772
1773 TC_LOG_INFO("server.loading", ">> Loaded {} creature model based info in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1774}
1775
1777{
1778 uint32 oldMSTime = getMSTime();
1779
1780 QueryResult result = WorldDatabase.Query("SELECT TotemSlot, RaceId, DisplayId from player_totem_model");
1781
1782 if (!result)
1783 {
1784 TC_LOG_INFO("server.loading", ">> Loaded 0 player totem model records. DB table `player_totem_model` is empty.");
1785 return;
1786 }
1787
1788 uint32 count = 0;
1789 do
1790 {
1791 Field* fields = result->Fetch();
1792
1793 SummonSlot totemSlot = SummonSlot(fields[0].GetUInt8());
1794 uint8 race = fields[1].GetUInt8();
1795 uint32 displayId = fields[2].GetUInt32();
1796
1797 if (totemSlot < SUMMON_SLOT_TOTEM_FIRE || totemSlot >= MAX_TOTEM_SLOT)
1798 {
1799 TC_LOG_ERROR("sql.sql", "Wrong TotemSlot {} in `player_totem_model` table, skipped.", totemSlot);
1800 continue;
1801 }
1802
1803 ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race);
1804 if (!raceEntry)
1805 {
1806 TC_LOG_ERROR("sql.sql", "Race {} defined in `player_totem_model` does not exists, skipped.", uint32(race));
1807 continue;
1808 }
1809
1810 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(displayId);
1811 if (!displayEntry)
1812 {
1813 TC_LOG_ERROR("sql.sql", "TotemSlot: {} defined in `player_totem_model` has non-existing model ({}), skipped.", totemSlot, displayId);
1814 continue;
1815 }
1816
1817 _playerTotemModel[std::make_pair(totemSlot, Races(race))] = displayId;
1818 ++count;
1819 }
1820 while (result->NextRow());
1821
1822 TC_LOG_INFO("server.loading", ">> Loaded {} player totem model records in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
1823}
1824
1826{
1827 auto itr = _playerTotemModel.find(std::make_pair(totemSlot, race));
1828 if (itr != _playerTotemModel.end())
1829 return itr->second;
1830 return 0;
1831}
1832
1834{
1835 uint32 oldMSTime = getMSTime();
1836
1837 _linkedRespawnStore.clear();
1838 // 0 1 2
1839 QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");
1840
1841 if (!result)
1842 {
1843 TC_LOG_INFO("server.loading", ">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
1844 return;
1845 }
1846
1847 do
1848 {
1849 Field* fields = result->Fetch();
1850
1851 ObjectGuid::LowType guidLow = fields[0].GetUInt32();
1852 ObjectGuid::LowType linkedGuidLow = fields[1].GetUInt32();
1853 uint8 linkType = fields[2].GetUInt8();
1854
1855 ObjectGuid guid, linkedGuid;
1856 bool error = false;
1857 switch (linkType)
1858 {
1860 {
1861 CreatureData const* slave = GetCreatureData(guidLow);
1862 if (!slave)
1863 {
1864 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1865 error = true;
1866 break;
1867 }
1868
1869 CreatureData const* master = GetCreatureData(linkedGuidLow);
1870 if (!master)
1871 {
1872 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1873 error = true;
1874 break;
1875 }
1876
1877 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1878 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1879 {
1880 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1881 error = true;
1882 break;
1883 }
1884
1885 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1886 {
1887 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1888 error = true;
1889 break;
1890 }
1891
1892 guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
1893 linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
1894 break;
1895 }
1897 {
1898 CreatureData const* slave = GetCreatureData(guidLow);
1899 if (!slave)
1900 {
1901 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (guid) '{}' not found in creature table", guidLow);
1902 error = true;
1903 break;
1904 }
1905
1906 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1907 if (!master)
1908 {
1909 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1910 error = true;
1911 break;
1912 }
1913
1914 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1915 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1916 {
1917 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1918 error = true;
1919 break;
1920 }
1921
1922 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1923 {
1924 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1925 error = true;
1926 break;
1927 }
1928
1929 guid = ObjectGuid::Create<HighGuid::Unit>(slave->id, guidLow);
1930 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->id, linkedGuidLow);
1931 break;
1932 }
1934 {
1935 GameObjectData const* slave = GetGameObjectData(guidLow);
1936 if (!slave)
1937 {
1938 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1939 error = true;
1940 break;
1941 }
1942
1943 GameObjectData const* master = GetGameObjectData(linkedGuidLow);
1944 if (!master)
1945 {
1946 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (linkedGuid) '{}' not found in gameobject table", linkedGuidLow);
1947 error = true;
1948 break;
1949 }
1950
1951 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1952 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1953 {
1954 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1955 error = true;
1956 break;
1957 }
1958
1959 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1960 {
1961 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Gameobject '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1962 error = true;
1963 break;
1964 }
1965
1966 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->id, guidLow);
1967 linkedGuid = ObjectGuid::Create<HighGuid::GameObject>(master->id, linkedGuidLow);
1968 break;
1969 }
1971 {
1972 GameObjectData const* slave = GetGameObjectData(guidLow);
1973 if (!slave)
1974 {
1975 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject (guid) '{}' not found in gameobject table", guidLow);
1976 error = true;
1977 break;
1978 }
1979
1980 CreatureData const* master = GetCreatureData(linkedGuidLow);
1981 if (!master)
1982 {
1983 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature (linkedGuid) '{}' not found in creature table", linkedGuidLow);
1984 error = true;
1985 break;
1986 }
1987
1988 MapEntry const* const map = sMapStore.LookupEntry(master->mapId);
1989 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
1990 {
1991 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' on an unpermitted map.", guidLow, linkedGuidLow);
1992 error = true;
1993 break;
1994 }
1995
1996 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
1997 {
1998 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Gameobject '{}' linking to Creature '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
1999 error = true;
2000 break;
2001 }
2002
2003 guid = ObjectGuid::Create<HighGuid::GameObject>(slave->id, guidLow);
2004 linkedGuid = ObjectGuid::Create<HighGuid::Unit>(master->id, linkedGuidLow);
2005 break;
2006 }
2007 }
2008
2009 if (!error)
2010 _linkedRespawnStore[guid] = linkedGuid;
2011 }
2012 while (result->NextRow());
2013
2014 TC_LOG_INFO("server.loading", ">> Loaded {} linked respawns in {} ms", uint64(_linkedRespawnStore.size()), GetMSTimeDiffToNow(oldMSTime));
2015}
2016
2018{
2019 if (!guidLow)
2020 return false;
2021
2022 CreatureData const* master = GetCreatureData(guidLow);
2023 ASSERT(master);
2024 ObjectGuid guid = ObjectGuid::Create<HighGuid::Unit>(master->id, guidLow);
2025
2026 if (!linkedGuidLow) // we're removing the linking
2027 {
2028 _linkedRespawnStore.erase(guid);
2030 stmt->setUInt32(0, guidLow);
2032 WorldDatabase.Execute(stmt);
2033 return true;
2034 }
2035
2036 CreatureData const* slave = GetCreatureData(linkedGuidLow);
2037 if (!slave)
2038 {
2039 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to non-existent creature '{}'.", guidLow, linkedGuidLow);
2040 return false;
2041 }
2042
2043 MapEntry const* map = sMapStore.LookupEntry(master->mapId);
2044 if (!map || !map->Instanceable() || (master->mapId != slave->mapId))
2045 {
2046 TC_LOG_ERROR("sql.sql", "Creature '{}' linking to '{}' on an unpermitted map.", guidLow, linkedGuidLow);
2047 return false;
2048 }
2049
2050 if (!(master->spawnMask & slave->spawnMask)) // they must have a possibility to meet (normal/heroic difficulty)
2051 {
2052 TC_LOG_ERROR("sql.sql", "LinkedRespawn: Creature '{}' linking to '{}' with not corresponding spawnMask", guidLow, linkedGuidLow);
2053 return false;
2054 }
2055
2056 ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id, linkedGuidLow);
2057
2058 _linkedRespawnStore[guid] = linkedGuid;
2060 stmt->setUInt32(0, guidLow);
2061 stmt->setUInt32(1, linkedGuidLow);
2063 WorldDatabase.Execute(stmt);
2064 return true;
2065}
2066
2068{
2069 uint32 oldMSTime = getMSTime();
2070
2071 _tempSummonDataStore.clear(); // needed for reload case
2072
2073 // 0 1 2 3 4 5 6 7 8 9
2074 QueryResult result = WorldDatabase.Query("SELECT summonerId, summonerType, groupId, entry, position_x, position_y, position_z, orientation, summonType, summonTime FROM creature_summon_groups");
2075
2076 if (!result)
2077 {
2078 TC_LOG_INFO("server.loading", ">> Loaded 0 temp summons. DB table `creature_summon_groups` is empty.");
2079 return;
2080 }
2081
2082 uint32 count = 0;
2083 do
2084 {
2085 Field* fields = result->Fetch();
2086
2087 uint32 summonerId = fields[0].GetUInt32();
2088 SummonerType summonerType = SummonerType(fields[1].GetUInt8());
2089 uint8 group = fields[2].GetUInt8();
2090
2091 switch (summonerType)
2092 {
2094 if (!GetCreatureTemplate(summonerId))
2095 {
2096 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for creature summoner type, skipped.", summonerId);
2097 continue;
2098 }
2099 break;
2101 if (!GetGameObjectTemplate(summonerId))
2102 {
2103 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for gameobject summoner type, skipped.", summonerId);
2104 continue;
2105 }
2106 break;
2107 case SUMMONER_TYPE_MAP:
2108 if (!sMapStore.LookupEntry(summonerId))
2109 {
2110 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has summoner with non existing entry {} for map summoner type, skipped.", summonerId);
2111 continue;
2112 }
2113 break;
2114 default:
2115 TC_LOG_ERROR("sql.sql", "Table `creature_summon_groups` has unhandled summoner type {} for summoner {}, skipped.", summonerType, summonerId);
2116 continue;
2117 }
2118
2119 TempSummonData data;
2120 data.entry = fields[3].GetUInt32();
2121
2122 if (!GetCreatureTemplate(data.entry))
2123 {
2124 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);
2125 continue;
2126 }
2127
2128 float posX = fields[4].GetFloat();
2129 float posY = fields[5].GetFloat();
2130 float posZ = fields[6].GetFloat();
2131 float orientation = fields[7].GetFloat();
2132
2133 data.pos.Relocate(posX, posY, posZ, orientation);
2134
2135 data.type = TempSummonType(fields[8].GetUInt8());
2136
2138 {
2139 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);
2140 continue;
2141 }
2142
2143 data.time = fields[9].GetUInt32();
2144
2145 TempSummonGroupKey key(summonerId, summonerType, group);
2146 _tempSummonDataStore[key].push_back(data);
2147
2148 ++count;
2149
2150 } while (result->NextRow());
2151
2152 TC_LOG_INFO("server.loading", ">> Loaded {} temp summons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
2153}
2154
2156{
2157 uint32 oldMSTime = getMSTime();
2158
2159 // 0 1 2 3 4 5 6 7 8 9 10
2160 QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, position_x, position_y, position_z, orientation, modelid, equipment_id, spawntimesecs, wander_distance, "
2161 // 11 12 13 14 15 16 17 18 19 20 21
2162 "currentwaypoint, curhealth, curmana, MovementType, spawnMask, phaseMask, eventEntry, poolSpawnId, creature.npcflag, creature.unit_flags, creature.dynamicflags, "
2163 // 22 23
2164 "creature.ScriptName, creature.StringId "
2165 "FROM creature "
2166 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
2167 "LEFT OUTER JOIN pool_members ON pool_members.type = 0 AND creature.guid = pool_members.spawnId");
2168
2169 if (!result)
2170 {
2171 TC_LOG_INFO("server.loading", ">> Loaded 0 creatures. DB table `creature` is empty.");
2172 return;
2173 }
2174
2175 // Build single time for check spawnmask
2176 std::map<uint32, uint32> spawnMasks;
2177 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
2178 if (sMapStore.LookupEntry(i))
2179 for (uint8 k = 0; k < MAX_DIFFICULTY; ++k)
2181 spawnMasks[i] |= (1 << k);
2182
2183 _creatureDataStore.rehash(result->GetRowCount());
2184
2185 do
2186 {
2187 Field* fields = result->Fetch();
2188
2189 ObjectGuid::LowType guid = fields[0].GetUInt32();
2190 uint32 entry = fields[1].GetUInt32();
2191
2192 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2193 if (!cInfo)
2194 {
2195 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) with non existing creature entry {}, skipped.", guid, entry);
2196 continue;
2197 }
2198
2199 CreatureData& data = _creatureDataStore[guid];
2200 data.spawnId = guid;
2201 data.id = entry;
2202 data.mapId = fields[2].GetUInt16();
2203 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2204 data.displayid = fields[7].GetUInt32();
2205 data.equipmentId = fields[8].GetInt8();
2206 data.spawntimesecs = fields[9].GetUInt32();
2207 data.wander_distance = fields[10].GetFloat();
2208 data.currentwaypoint= fields[11].GetUInt32();
2209 data.curhealth = fields[12].GetUInt32();
2210 data.curmana = fields[13].GetUInt32();
2211 data.movementType = fields[14].GetUInt8();
2212 data.spawnMask = fields[15].GetUInt8();
2213 data.phaseMask = fields[16].GetUInt32();
2214 int16 gameEvent = fields[17].GetInt8();
2215 uint32 PoolId = fields[18].GetUInt32();
2216 data.npcflag = fields[19].GetUInt32();
2217 data.unit_flags = fields[20].GetUInt32();
2218 data.dynamicflags = fields[21].GetUInt32();
2219 data.scriptId = GetScriptId(fields[22].GetString());
2220 data.StringId = fields[23].GetString();
2222
2223 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2224 if (!mapEntry)
2225 {
2226 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that spawned at nonexistent map (Id: {}), skipped.", guid, data.mapId);
2227 continue;
2228 }
2229
2230 // Skip spawnMask check for transport maps
2231 if (!IsTransportMap(data.mapId))
2232 {
2233 if (data.spawnMask & ~spawnMasks[data.mapId])
2234 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);
2235 }
2236 else
2237 data.spawnGroupData = GetLegacySpawnGroup(); // force compatibility group for transport spawns
2238
2239 bool ok = true;
2240 for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
2241 {
2242 if (_difficultyEntries[diff].find(data.id) != _difficultyEntries[diff].end())
2243 {
2244 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {}) that is listed as difficulty {} template (entry: {}) in `creature_template`, skipped.",
2245 guid, diff + 1, data.id);
2246 ok = false;
2247 }
2248 }
2249 if (!ok)
2250 continue;
2251
2252 // -1 random, 0 no equipment
2253 if (data.equipmentId != 0)
2254 {
2255 if (!GetEquipmentInfo(data.id, data.equipmentId))
2256 {
2257 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);
2258 data.equipmentId = 0;
2259 }
2260 }
2261
2263 {
2264 if (!mapEntry->IsDungeon())
2265 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);
2266 }
2267
2268 if (data.movementType >= MAX_DB_MOTION_TYPE)
2269 {
2270 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);
2272 }
2273
2274 if (data.wander_distance < 0.0f)
2275 {
2276 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `wander_distance`< 0, set to 0.", guid, data.id);
2277 data.wander_distance = 0.0f;
2278 }
2279 else if (data.movementType == RANDOM_MOTION_TYPE)
2280 {
2281 if (data.wander_distance == 0.0f)
2282 {
2283 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `MovementType`=1 (random movement) but with `wander_distance`=0, replace by idle movement type (0).", guid, data.id);
2285 }
2286 }
2287 else if (data.movementType == IDLE_MOTION_TYPE)
2288 {
2289 if (data.wander_distance != 0.0f)
2290 {
2291 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);
2292 data.wander_distance = 0.0f;
2293 }
2294 }
2295
2296 if (data.phaseMask == 0)
2297 {
2298 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
2299 data.phaseMask = 1;
2300 }
2301
2302 if (uint32 disallowedUnitFlags = (data.unit_flags & ~UNIT_FLAG_ALLOWED))
2303 {
2304 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with disallowed `unit_flags` {}, removing incorrect flag.", guid, data.id, disallowedUnitFlags);
2306 }
2307
2308 if (data.dynamicflags)
2309 {
2310 TC_LOG_ERROR("sql.sql", "Table `creature` has creature (GUID: {} Entry: {}) with `dynamicflags` > 0. Ignored and set to 0.", guid, data.id);
2311 data.dynamicflags = 0;
2312 }
2313
2315 {
2316 uint32 zoneId = 0;
2317 uint32 areaId = 0;
2318 sMapMgr->GetZoneAndAreaId(data.phaseMask, zoneId, areaId, data.mapId, data.spawnPoint);
2319
2321
2322 stmt->setUInt32(0, zoneId);
2323 stmt->setUInt32(1, areaId);
2324 stmt->setUInt64(2, guid);
2325
2326 WorldDatabase.Execute(stmt);
2327 }
2328
2329 // Add to grid if not managed by the game event or pool system
2330 if (gameEvent == 0 && PoolId == 0)
2331 AddCreatureToGrid(guid, &data);
2332 }
2333 while (result->NextRow());
2334
2335 TC_LOG_INFO("server.loading", ">> Loaded {} creatures in {} ms", _creatureDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2336}
2337
2339{
2341 return Trinity::Containers::MapGetValuePtr(*mapGuids, cell_id);
2342
2343 return nullptr;
2344}
2345
2350
2352{
2353 uint8 mask = data->spawnMask;
2354 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2355 {
2356 if (mask & 1)
2357 {
2359 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2360 cell_guids.creatures.insert(guid);
2361 }
2362 }
2363}
2364
2366{
2367 uint8 mask = data->spawnMask;
2368 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2369 {
2370 if (mask & 1)
2371 {
2373 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2374 cell_guids.creatures.erase(guid);
2375 }
2376 }
2377}
2378
2379ObjectGuid::LowType ObjectMgr::AddGameObjectData(uint32 entry, uint32 mapId, Position const& pos, QuaternionData const& rot, uint32 spawntimedelay /*= 0*/)
2380{
2381 GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
2382 if (!goinfo)
2383 return 0;
2384
2385 Map* map = sMapMgr->CreateBaseMap(mapId);
2386 if (!map)
2387 return 0;
2388
2390
2392 data.spawnId = spawnId;
2393 data.id = entry;
2394 data.mapId = mapId;
2395 data.spawnPoint.Relocate(pos);
2396 data.rotation = rot;
2397 data.spawntimesecs = spawntimedelay;
2398 data.animprogress = 100;
2399 data.spawnMask = 1;
2400 data.goState = GO_STATE_READY;
2402 data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
2403 data.dbData = false;
2405
2406 AddGameobjectToGrid(spawnId, &data);
2407
2408 // Spawn if necessary (loaded grids only)
2409 // We use spawn coords to spawn
2410 if (!map->Instanceable() && map->IsGridLoaded(data.spawnPoint))
2411 {
2412 GameObject* go = new GameObject;
2413 if (!go->LoadFromDB(spawnId, map, true))
2414 {
2415 TC_LOG_ERROR("misc", "AddGameObjectData: cannot add gameobject entry {} to map", entry);
2416 delete go;
2417 return 0;
2418 }
2419 }
2420
2421 TC_LOG_DEBUG("maps", "AddGameObjectData: dbguid {} entry {} map {} pos {}", spawnId, entry, mapId, data.spawnPoint.ToString());
2422
2423 return spawnId;
2424}
2425
2426ObjectGuid::LowType ObjectMgr::AddCreatureData(uint32 entry, uint32 mapId, Position const& pos, uint32 spawntimedelay /*= 0*/)
2427{
2428 CreatureTemplate const* cInfo = GetCreatureTemplate(entry);
2429 if (!cInfo)
2430 return 0;
2431
2432 uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
2433 CreatureBaseStats const* stats = GetCreatureBaseStats(level, cInfo->unit_class);
2434 Map* map = sMapMgr->CreateBaseMap(mapId);
2435 if (!map)
2436 return 0;
2437
2439 CreatureData& data = NewOrExistCreatureData(spawnId);
2440 data.spawnId = spawnId;
2441 data.id = entry;
2442 data.mapId = mapId;
2443 data.spawnPoint.Relocate(pos);
2444 data.displayid = 0;
2445 data.equipmentId = 0;
2446 data.spawntimesecs = spawntimedelay;
2447 data.wander_distance = 0;
2448 data.currentwaypoint = 0;
2449 data.curhealth = stats->GenerateHealth(cInfo);
2450 data.curmana = stats->GenerateMana(cInfo);
2451 data.movementType = cInfo->MovementType;
2452 data.spawnMask = 1;
2454 data.dbData = false;
2455 data.npcflag = cInfo->npcflag;
2456 data.unit_flags = cInfo->unit_flags;
2457 data.dynamicflags = cInfo->dynamicflags;
2459
2460 AddCreatureToGrid(spawnId, &data);
2461
2462 // We use spawn coords to spawn
2463 if (!map->Instanceable() && !map->IsRemovalGrid(data.spawnPoint))
2464 {
2465 Creature* creature = new Creature();
2466 if (!creature->LoadFromDB(spawnId, map, true, true))
2467 {
2468 TC_LOG_ERROR("misc", "AddCreature: Cannot add creature entry {} to map", entry);
2469 delete creature;
2470 return 0;
2471 }
2472 }
2473
2474 return spawnId;
2475}
2476
2478{
2479 uint32 oldMSTime = getMSTime();
2480
2481 // 0 1 2 3 4 5 6
2482 QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
2483 // 7 8 9 10 11 12 13 14 15 16 17
2484 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, eventEntry, poolSpawnId, "
2485 // 18 19
2486 "ScriptName, StringId "
2487 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
2488 "LEFT OUTER JOIN pool_members ON pool_members.type = 1 AND gameobject.guid = pool_members.spawnId");
2489
2490 if (!result)
2491 {
2492 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
2493 return;
2494 }
2495
2496 // build single time for check spawnmask
2497 std::map<uint32, uint32> spawnMasks;
2498 for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
2499 if (sMapStore.LookupEntry(i))
2500 for (uint8 k = 0; k < MAX_DIFFICULTY; ++k)
2502 spawnMasks[i] |= (1 << k);
2503
2504 _gameObjectDataStore.rehash(result->GetRowCount());
2505
2506 do
2507 {
2508 Field* fields = result->Fetch();
2509
2510 ObjectGuid::LowType guid = fields[0].GetUInt32();
2511 uint32 entry = fields[1].GetUInt32();
2512
2513 GameObjectTemplate const* gInfo = GetGameObjectTemplate(entry);
2514 if (!gInfo)
2515 {
2516 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {}) with non existing gameobject entry {}, skipped.", guid, entry);
2517 continue;
2518 }
2519
2520 if (!gInfo->displayId)
2521 {
2522 switch (gInfo->type)
2523 {
2526 break;
2527 default:
2528 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) doesn't have a displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2529 break;
2530 }
2531 }
2532
2533 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
2534 {
2535 TC_LOG_ERROR("sql.sql", "Gameobject (GUID: {} Entry {} GoType: {}) has an invalid displayId ({}), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
2536 continue;
2537 }
2538
2540
2541 data.spawnId = guid;
2542 data.id = entry;
2543 data.mapId = fields[2].GetUInt16();
2544 data.spawnPoint.Relocate(fields[3].GetFloat(), fields[4].GetFloat(), fields[5].GetFloat(), fields[6].GetFloat());
2545 data.rotation.x = fields[7].GetFloat();
2546 data.rotation.y = fields[8].GetFloat();
2547 data.rotation.z = fields[9].GetFloat();
2548 data.rotation.w = fields[10].GetFloat();
2549 data.spawntimesecs = fields[11].GetInt32();
2551
2552 MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapId);
2553 if (!mapEntry)
2554 {
2555 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) spawned on a non-existed map (Id: {}), skip", guid, data.id, data.mapId);
2556 continue;
2557 }
2558
2559 if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
2560 {
2561 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);
2562 }
2563
2564 data.animprogress = fields[12].GetUInt8();
2565 data.artKit = 0;
2566
2567 uint32 go_state = fields[13].GetUInt8();
2568 if (go_state >= MAX_GO_STATE)
2569 {
2570 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid `state` ({}) value, skip", guid, data.id, go_state);
2571 continue;
2572 }
2573 data.goState = GOState(go_state);
2574
2575 data.spawnMask = fields[14].GetUInt8();
2576
2577 if (!IsTransportMap(data.mapId))
2578 {
2579 if (data.spawnMask & ~spawnMasks[data.mapId])
2580 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);
2581 }
2582 else
2583 data.spawnGroupData = GetLegacySpawnGroup(); // force compatibility group for transport spawns
2584
2585 data.phaseMask = fields[15].GetUInt32();
2586 int16 gameEvent = fields[16].GetInt8();
2587 uint32 PoolId = fields[17].GetUInt32();
2588
2589 data.scriptId = GetScriptId(fields[18].GetString());
2590 data.StringId = fields[19].GetString();
2591
2592 if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
2593 {
2594 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationX ({}) value, skip", guid, data.id, data.rotation.x);
2595 continue;
2596 }
2597
2598 if (data.rotation.y < -1.0f || data.rotation.y > 1.0f)
2599 {
2600 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationY ({}) value, skip", guid, data.id, data.rotation.y);
2601 continue;
2602 }
2603
2604 if (data.rotation.z < -1.0f || data.rotation.z > 1.0f)
2605 {
2606 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationZ ({}) value, skip", guid, data.id, data.rotation.z);
2607 continue;
2608 }
2609
2610 if (data.rotation.w < -1.0f || data.rotation.w > 1.0f)
2611 {
2612 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotationW ({}) value, skip", guid, data.id, data.rotation.w);
2613 continue;
2614 }
2615
2617 {
2618 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skip", guid, data.id);
2619 continue;
2620 }
2621
2622 if (!data.rotation.isUnit())
2623 {
2624 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);
2626 }
2627
2628 if (data.phaseMask == 0)
2629 {
2630 TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
2631 data.phaseMask = 1;
2632 }
2633
2635 {
2636 uint32 zoneId = 0;
2637 uint32 areaId = 0;
2638 sMapMgr->GetZoneAndAreaId(data.phaseMask, zoneId, areaId, data.mapId, data.spawnPoint);
2639
2641
2642 stmt->setUInt32(0, zoneId);
2643 stmt->setUInt32(1, areaId);
2644 stmt->setUInt64(2, guid);
2645
2646 WorldDatabase.Execute(stmt);
2647 }
2648
2649 if (gameEvent == 0 && PoolId == 0) // if not this is to be managed by GameEvent System or Pool system
2650 AddGameobjectToGrid(guid, &data);
2651 }
2652 while (result->NextRow());
2653
2654 TC_LOG_INFO("server.loading", ">> Loaded {} gameobjects in {} ms", _gameObjectDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2655}
2656
2658{
2659 uint32 oldMSTime = getMSTime();
2660
2661 // 0 1 2
2662 QueryResult result = WorldDatabase.Query("SELECT groupId, groupName, groupFlags FROM spawn_group_template");
2663
2664 if (result)
2665 {
2666 do
2667 {
2668 Field* fields = result->Fetch();
2669 uint32 groupId = fields[0].GetUInt32();
2671 group.groupId = groupId;
2672 group.name = fields[1].GetString();
2674 uint32 flags = fields[2].GetUInt32();
2676 {
2678 TC_LOG_ERROR("sql.sql", "Invalid spawn group flag {} on group ID {} ({}), reduced to valid flag {}.", flags, groupId, group.name, uint32(group.flags));
2679 }
2681 {
2682 flags &= ~SPAWNGROUP_FLAG_MANUAL_SPAWN;
2683 TC_LOG_ERROR("sql.sql", "System spawn group {} ({}) has invalid manual spawn flag. Ignored.", groupId, group.name);
2684 }
2685 group.flags = SpawnGroupFlags(flags);
2686 } while (result->NextRow());
2687 }
2688
2689 if (_spawnGroupDataStore.find(0) == _spawnGroupDataStore.end())
2690 {
2691 TC_LOG_ERROR("sql.sql", "Default spawn group (index 0) is missing from DB! Manually inserted.");
2693 data.groupId = 0;
2694 data.name = "Default Group";
2695 data.mapId = 0;
2697 }
2698 if (_spawnGroupDataStore.find(1) == _spawnGroupDataStore.end())
2699 {
2700 TC_LOG_ERROR("sql.sql", "Default legacy spawn group (index 1) is missing from DB! Manually inserted.");
2702 data.groupId = 1;
2703 data.name = "Legacy Group";
2704 data.mapId = 0;
2706 }
2707
2708 if (result)
2709 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group templates in {} ms", _spawnGroupDataStore.size(), GetMSTimeDiffToNow(oldMSTime));
2710 else
2711 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group templates. DB table `spawn_group_template` is empty.");
2712
2713 return;
2714}
2715
2717{
2718 uint32 oldMSTime = getMSTime();
2719
2720 // 0 1 2
2721 QueryResult result = WorldDatabase.Query("SELECT groupId, spawnType, spawnId FROM spawn_group");
2722
2723 if (!result)
2724 {
2725 TC_LOG_INFO("server.loading", ">> Loaded 0 spawn group members. DB table `spawn_group` is empty.");
2726 return;
2727 }
2728
2729 uint32 numMembers = 0;
2730 do
2731 {
2732 Field* fields = result->Fetch();
2733 uint32 groupId = fields[0].GetUInt32();
2734 SpawnObjectType spawnType = SpawnObjectType(fields[1].GetUInt8());
2735 if (!SpawnData::TypeIsValid(spawnType))
2736 {
2737 TC_LOG_ERROR("sql.sql", "Spawn data with invalid type {} listed for spawn group {}. Skipped.", uint32(spawnType), groupId);
2738 continue;
2739 }
2740 ObjectGuid::LowType spawnId = fields[2].GetUInt32();
2741
2742 SpawnMetadata const* data = GetSpawnMetadata(spawnType, spawnId);
2743 if (!data)
2744 {
2745 TC_LOG_ERROR("sql.sql", "Spawn data with ID ({},{}) not found, but is listed as a member of spawn group {}!", uint32(spawnType), spawnId, groupId);
2746 continue;
2747 }
2748 else if (data->spawnGroupData->groupId)
2749 {
2750 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);
2751 continue;
2752 }
2753 auto it = _spawnGroupDataStore.find(groupId);
2754 if (it == _spawnGroupDataStore.end())
2755 {
2756 TC_LOG_ERROR("sql.sql", "Spawn group {} assigned to spawn ID ({},{}), but group does not exist!", groupId, uint32(spawnType), spawnId);
2757 continue;
2758 }
2759 else
2760 {
2761 SpawnGroupTemplateData& groupTemplate = it->second;
2762 if (groupTemplate.mapId == SPAWNGROUP_MAP_UNSET)
2763 groupTemplate.mapId = data->mapId;
2764 else if (groupTemplate.mapId != data->mapId && !(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2765 {
2766 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);
2767 continue;
2768 }
2769 const_cast<SpawnMetadata*>(data)->spawnGroupData = &groupTemplate;
2770 if (!(groupTemplate.flags & SPAWNGROUP_FLAG_SYSTEM))
2771 _spawnGroupMapStore.emplace(groupId, data);
2772 ++numMembers;
2773 }
2774 } while (result->NextRow());
2775
2776 TC_LOG_INFO("server.loading", ">> Loaded {} spawn group members in {} ms", numMembers, GetMSTimeDiffToNow(oldMSTime));
2777}
2778
2780{
2781 uint32 oldMSTime = getMSTime();
2782
2783 // 0 1 2 3 4
2784 QueryResult result = WorldDatabase.Query("SELECT instanceMapId, bossStateId, bossStates, spawnGroupId, flags FROM instance_spawn_groups");
2785
2786 if (!result)
2787 {
2788 TC_LOG_INFO("server.loading", ">> Loaded 0 instance spawn groups. DB table `instance_spawn_groups` is empty.");
2789 return;
2790 }
2791
2792 uint32 n = 0;
2793 do
2794 {
2795 Field* fields = result->Fetch();
2796 uint32 const spawnGroupId = fields[3].GetUInt32();
2797 auto it = _spawnGroupDataStore.find(spawnGroupId);
2798 if (it == _spawnGroupDataStore.end() || (it->second.flags & SPAWNGROUP_FLAG_SYSTEM))
2799 {
2800 TC_LOG_ERROR("sql.sql", "Invalid spawn group {} specified for instance {}. Skipped.", spawnGroupId, fields[0].GetUInt16());
2801 continue;
2802 }
2803
2804 uint16 const instanceMapId = fields[0].GetUInt16();
2805 if (it->second.mapId != instanceMapId)
2806 {
2807 TC_LOG_ERROR("sql.sql", "Instance spawn group {} specified for instance {} has spawns on a different map {}. Skipped.",
2808 spawnGroupId, instanceMapId, it->second.mapId);
2809 continue;
2810 }
2811
2812 InstanceSpawnGroupInfo& info = _instanceSpawnGroupStore[instanceMapId].emplace_back();
2813 info.SpawnGroupId = spawnGroupId;
2814 info.BossStateId = fields[1].GetUInt8();
2815
2816 uint8 const ALL_STATES = (1 << TO_BE_DECIDED) - 1;
2817 uint8 const states = fields[2].GetUInt8();
2818 if (states & ~ALL_STATES)
2819 {
2820 info.BossStates = states & ALL_STATES;
2821 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid boss state mask {} - truncated to {}.", instanceMapId, spawnGroupId, states, info.BossStates);
2822 }
2823 else
2824 info.BossStates = states;
2825
2826 uint8 const flags = fields[4].GetUInt8();
2828 {
2830 TC_LOG_ERROR("sql.sql", "Instance spawn group ({},{}) had invalid flags {} - truncated to {}.", instanceMapId, spawnGroupId, flags, info.Flags);
2831 }
2832 else
2833 info.Flags = flags;
2834
2836 {
2838 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);
2839 }
2840
2841 ++n;
2842 } while (result->NextRow());
2843
2844 TC_LOG_INFO("server.loading", ">> Loaded {} instance spawn groups in {} ms", n, GetMSTimeDiffToNow(oldMSTime));
2845}
2846
2848{
2849 auto templateIt = _spawnGroupDataStore.find(data->spawnGroupData->groupId);
2850 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);
2851 if (templateIt->second.flags & SPAWNGROUP_FLAG_SYSTEM) // system groups don't store their members in the map
2852 return;
2853
2854 auto pair = _spawnGroupMapStore.equal_range(data->spawnGroupData->groupId);
2855 for (auto it = pair.first; it != pair.second; ++it)
2856 {
2857 if (it->second != data)
2858 continue;
2859 _spawnGroupMapStore.erase(it);
2860 return;
2861 }
2862 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);
2863}
2864
2866{
2867 uint8 mask = data->spawnMask;
2868 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2869 {
2870 if (mask & 1)
2871 {
2873 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2874 cell_guids.gameobjects.insert(guid);
2875 }
2876 }
2877}
2878
2880{
2881 uint8 mask = data->spawnMask;
2882 for (uint8 i = 0; mask != 0; i++, mask >>= 1)
2883 {
2884 if (mask & 1)
2885 {
2887 CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(data->mapId, i)][cellCoord.GetId()];
2888 cell_guids.gameobjects.erase(guid);
2889 }
2890 }
2891}
2892
2894{
2895 uint32 oldMSTime = getMSTime();
2896
2897 _itemLocaleStore.clear(); // need for reload case
2898
2899 // 0 1 2 3
2900 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name, Description FROM item_template_locale");
2901 if (!result)
2902 return;
2903
2904 do
2905 {
2906 Field* fields = result->Fetch();
2907
2908 uint32 id = fields[0].GetUInt32();
2909 std::string localeName = fields[1].GetString();
2910
2911 LocaleConstant locale = GetLocaleByName(localeName);
2912 if (locale == LOCALE_enUS)
2913 continue;
2914
2915 ItemLocale& data = _itemLocaleStore[id];
2916 AddLocaleString(fields[2].GetString(), locale, data.Name);
2917 AddLocaleString(fields[3].GetString(), locale, data.Description);
2918 } while (result->NextRow());
2919
2920 TC_LOG_INFO("server.loading", ">> Loaded {} Item locale strings in {} ms", uint32(_itemLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
2921}
2922
2924{
2925 uint32 oldMSTime = getMSTime();
2926
2927 // 0 1 2 3 4 5 6 7 8 9 10 11 12
2928 QueryResult result = WorldDatabase.Query("SELECT entry, class, subclass, SoundOverrideSubclass, name, displayid, Quality, Flags, FlagsExtra, BuyCount, BuyPrice, SellPrice, InventoryType, "
2929 // 13 14 15 16 17 18 19 20
2930 "AllowableClass, AllowableRace, ItemLevel, RequiredLevel, RequiredSkill, RequiredSkillRank, requiredspell, requiredhonorrank, "
2931 // 21 22 23 24 25 26 27 28
2932 "RequiredCityRank, RequiredReputationFaction, RequiredReputationRank, maxcount, stackable, ContainerSlots, StatsCount, stat_type1, "
2933 // 29 30 31 32 33 34 35 36 37 38
2934 "stat_value1, stat_type2, stat_value2, stat_type3, stat_value3, stat_type4, stat_value4, stat_type5, stat_value5, stat_type6, "
2935 // 39 40 41 42 43 44 45 46 47
2936 "stat_value6, stat_type7, stat_value7, stat_type8, stat_value8, stat_type9, stat_value9, stat_type10, stat_value10, "
2937 // 48 49 50 51 52 53 54 55 56 57 58
2938 "ScalingStatDistribution, ScalingStatValue, dmg_min1, dmg_max1, dmg_type1, dmg_min2, dmg_max2, dmg_type2, armor, holy_res, fire_res, "
2939 // 59 60 61 62 63 64 65 66 67 68
2940 "nature_res, frost_res, shadow_res, arcane_res, delay, ammo_type, RangedModRange, spellid_1, spelltrigger_1, spellcharges_1, "
2941 // 69 70 71 72 73 74 75
2942 "spellppmRate_1, spellcooldown_1, spellcategory_1, spellcategorycooldown_1, spellid_2, spelltrigger_2, spellcharges_2, "
2943 // 76 77 78 79 80 81 82
2944 "spellppmRate_2, spellcooldown_2, spellcategory_2, spellcategorycooldown_2, spellid_3, spelltrigger_3, spellcharges_3, "
2945 // 83 84 85 86 87 88 89
2946 "spellppmRate_3, spellcooldown_3, spellcategory_3, spellcategorycooldown_3, spellid_4, spelltrigger_4, spellcharges_4, "
2947 // 90 91 92 93 94 95 96
2948 "spellppmRate_4, spellcooldown_4, spellcategory_4, spellcategorycooldown_4, spellid_5, spelltrigger_5, spellcharges_5, "
2949 // 97 98 99 100 101 102 103 104 105
2950 "spellppmRate_5, spellcooldown_5, spellcategory_5, spellcategorycooldown_5, bonding, description, PageText, LanguageID, PageMaterial, "
2951 // 106 107 108 109 110 111 112 113 114 115 116 117
2952 "startquest, lockid, Material, sheath, RandomProperty, RandomSuffix, block, itemset, MaxDurability, area, Map, BagFamily, "
2953 // 118 119 120 121 122 123 124 125
2954 "TotemCategory, socketColor_1, socketContent_1, socketColor_2, socketContent_2, socketColor_3, socketContent_3, socketBonus, "
2955 // 126 127 128 129 130 131 132 133
2956 "GemProperties, RequiredDisenchantSkill, ArmorDamageModifier, duration, ItemLimitCategory, HolidayId, ScriptName, DisenchantID, "
2957 // 134 135 136
2958 "FoodType, minMoneyLoot, maxMoneyLoot, flagsCustom FROM item_template");
2959
2960 if (!result)
2961 {
2962 TC_LOG_INFO("server.loading", ">> Loaded 0 item templates. DB table `item_template` is empty.");
2963 return;
2964 }
2965
2966 _itemTemplateStore.reserve(result->GetRowCount());
2967 bool enforceDBCAttributes = sWorld->getBoolConfig(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES);
2968
2969 do
2970 {
2971 Field* fields = result->Fetch();
2972
2973 uint32 entry = fields[0].GetUInt32();
2974 ItemTemplate& itemTemplate = _itemTemplateStore[entry];
2975
2976 itemTemplate.ItemId = entry;
2977 itemTemplate.Class = uint32(fields[1].GetUInt8());
2978 itemTemplate.SubClass = uint32(fields[2].GetUInt8());
2979 itemTemplate.SoundOverrideSubclass = int32(fields[3].GetInt8());
2980 itemTemplate.Name1 = fields[4].GetString();
2981 itemTemplate.DisplayInfoID = fields[5].GetUInt32();
2982 itemTemplate.Quality = uint32(fields[6].GetUInt8());
2983 itemTemplate.Flags = fields[7].GetUInt32();
2984 itemTemplate.Flags2 = fields[8].GetUInt32();
2985 itemTemplate.BuyCount = uint32(fields[9].GetUInt8());
2986 itemTemplate.BuyPrice = int32(fields[10].GetInt64());
2987 itemTemplate.SellPrice = fields[11].GetUInt32();
2988 itemTemplate.InventoryType = uint32(fields[12].GetUInt8());
2989 itemTemplate.AllowableClass = fields[13].GetInt32();
2990 itemTemplate.AllowableRace = fields[14].GetInt32();
2991 itemTemplate.ItemLevel = uint32(fields[15].GetUInt16());
2992 itemTemplate.RequiredLevel = uint32(fields[16].GetUInt8());
2993 itemTemplate.RequiredSkill = uint32(fields[17].GetUInt16());
2994 itemTemplate.RequiredSkillRank = uint32(fields[18].GetUInt16());
2995 itemTemplate.RequiredSpell = fields[19].GetUInt32();
2996 itemTemplate.RequiredHonorRank = fields[20].GetUInt32();
2997 itemTemplate.RequiredCityRank = fields[21].GetUInt32();
2998 itemTemplate.RequiredReputationFaction = uint32(fields[22].GetUInt16());
2999 itemTemplate.RequiredReputationRank = uint32(fields[23].GetUInt16());
3000 itemTemplate.MaxCount = fields[24].GetInt32();
3001 itemTemplate.Stackable = fields[25].GetInt32();
3002 itemTemplate.ContainerSlots = uint32(fields[26].GetUInt8());
3003 itemTemplate.StatsCount = uint32(fields[27].GetUInt8());
3004
3005 if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
3006 {
3007 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in statscount ({}), replace by hardcoded limit ({}).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
3008 itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
3009 }
3010
3011 for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
3012 {
3013 itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i*2].GetUInt8());
3014 itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i*2].GetInt16());
3015 }
3016
3017 itemTemplate.ScalingStatDistribution = uint32(fields[48].GetUInt16());
3018 itemTemplate.ScalingStatValue = fields[49].GetInt32();
3019
3020 for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
3021 {
3022 itemTemplate.Damage[i].DamageMin = fields[50 + i*3].GetFloat();
3023 itemTemplate.Damage[i].DamageMax = fields[51 + i*3].GetFloat();
3024 itemTemplate.Damage[i].DamageType = uint32(fields[52 + i*3].GetUInt8());
3025 }
3026
3027 itemTemplate.Armor = uint32(fields[56].GetUInt16());
3028 itemTemplate.HolyRes = uint32(fields[57].GetUInt8());
3029 itemTemplate.FireRes = uint32(fields[58].GetUInt8());
3030 itemTemplate.NatureRes = uint32(fields[59].GetUInt8());
3031 itemTemplate.FrostRes = uint32(fields[60].GetUInt8());
3032 itemTemplate.ShadowRes = uint32(fields[61].GetUInt8());
3033 itemTemplate.ArcaneRes = uint32(fields[62].GetUInt8());
3034 itemTemplate.Delay = uint32(fields[63].GetUInt16());
3035 itemTemplate.AmmoType = uint32(fields[64].GetUInt8());
3036 itemTemplate.RangedModRange = fields[65].GetFloat();
3037
3038 for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
3039 {
3040 itemTemplate.Effects[i].SpellID = fields[66 + i * 7].GetInt32();
3041 itemTemplate.Effects[i].TriggerType = uint32(fields[67 + i * 7].GetUInt8());
3042 itemTemplate.Effects[i].Charges = int32(fields[68 + i * 7].GetInt16());
3043 itemTemplate.Effects[i].SpellPPMRate = fields[69 + i * 7].GetFloat();
3044 itemTemplate.Effects[i].CoolDownMSec = fields[70 + i * 7].GetInt32();
3045 itemTemplate.Effects[i].SpellCategoryID = uint32(fields[71 + i * 7].GetUInt16());
3046 itemTemplate.Effects[i].CategoryCoolDownMSec = fields[72 + i * 7].GetInt32();
3047 }
3048
3049 itemTemplate.Bonding = uint32(fields[101].GetUInt8());
3050 itemTemplate.Description = fields[102].GetString();
3051 itemTemplate.PageText = fields[103].GetUInt32();
3052 itemTemplate.LanguageID = uint32(fields[104].GetUInt8());
3053 itemTemplate.PageMaterial = uint32(fields[105].GetUInt8());
3054 itemTemplate.StartQuest = fields[106].GetUInt32();
3055 itemTemplate.LockID = fields[107].GetUInt32();
3056 itemTemplate.Material = int32(fields[108].GetInt8());
3057 itemTemplate.Sheath = uint32(fields[109].GetUInt8());
3058 itemTemplate.RandomProperty = fields[110].GetUInt32();
3059 itemTemplate.RandomSuffix = fields[111].GetInt32();
3060 itemTemplate.Block = fields[112].GetUInt32();
3061 itemTemplate.ItemSet = fields[113].GetUInt32();
3062 itemTemplate.MaxDurability = uint32(fields[114].GetUInt16());
3063 itemTemplate.Area = fields[115].GetUInt32();
3064 itemTemplate.Map = uint32(fields[116].GetUInt16());
3065 itemTemplate.BagFamily = fields[117].GetUInt32();
3066 itemTemplate.TotemCategory = fields[118].GetUInt32();
3067
3068 for (uint8 i = 0; i < MAX_ITEM_PROTO_SOCKETS; ++i)
3069 {
3070 itemTemplate.Socket[i].Color = uint32(fields[119 + i*2].GetUInt8());
3071 itemTemplate.Socket[i].Content = fields[120 + i*2].GetUInt32();
3072 }
3073
3074 itemTemplate.socketBonus = fields[125].GetUInt32();
3075 itemTemplate.GemProperties = fields[126].GetUInt32();
3076 itemTemplate.RequiredDisenchantSkill = uint32(fields[127].GetInt16());
3077 itemTemplate.ArmorDamageModifier = fields[128].GetFloat();
3078 itemTemplate.Duration = fields[129].GetUInt32();
3079 itemTemplate.ItemLimitCategory = uint32(fields[130].GetInt16());
3080 itemTemplate.HolidayId = fields[131].GetUInt32();
3081 itemTemplate.ScriptId = sObjectMgr->GetScriptId(fields[132].GetString());
3082 itemTemplate.DisenchantID = fields[133].GetUInt32();
3083 itemTemplate.FoodType = uint32(fields[134].GetUInt8());
3084 itemTemplate.MinMoneyLoot = fields[135].GetUInt32();
3085 itemTemplate.MaxMoneyLoot = fields[136].GetUInt32();
3086 itemTemplate.FlagsCu = fields[137].GetUInt32();
3087
3088 // Checks
3089
3090 ItemEntry const* dbcitem = sItemStore.LookupEntry(entry);
3091
3092 if (dbcitem)
3093 {
3094 if (itemTemplate.Class != dbcitem->ClassID)
3095 {
3096 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct class {}, must be {} .", entry, itemTemplate.Class, dbcitem->ClassID);
3097 if (enforceDBCAttributes)
3098 itemTemplate.Class = dbcitem->ClassID;
3099 }
3100
3101 if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclassID)
3102 {
3103 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct SoundOverrideSubclass ({}), must be {} .", entry, itemTemplate.SoundOverrideSubclass, dbcitem->SoundOverrideSubclassID);
3104 if (enforceDBCAttributes)
3105 itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclassID;
3106 }
3107 if (itemTemplate.Material != dbcitem->Material)
3108 {
3109 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct material ({}), must be {} .", entry, itemTemplate.Material, dbcitem->Material);
3110 if (enforceDBCAttributes)
3111 itemTemplate.Material = dbcitem->Material;
3112 }
3113 if (itemTemplate.InventoryType != dbcitem->InventoryType)
3114 {
3115 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct inventory type ({}), must be {} .", entry, itemTemplate.InventoryType, dbcitem->InventoryType);
3116 if (enforceDBCAttributes)
3117 itemTemplate.InventoryType = dbcitem->InventoryType;
3118 }
3119 if (itemTemplate.DisplayInfoID != dbcitem->DisplayInfoID)
3120 {
3121 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct display id ({}), must be {} .", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayInfoID);
3122 if (enforceDBCAttributes)
3123 itemTemplate.DisplayInfoID = dbcitem->DisplayInfoID;
3124 }
3125 if (itemTemplate.Sheath != dbcitem->SheatheType)
3126 {
3127 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct sheathid ({}), must be {} .", entry, itemTemplate.Sheath, dbcitem->SheatheType);
3128 if (enforceDBCAttributes)
3129 itemTemplate.Sheath = dbcitem->SheatheType;
3130 }
3131
3132 }
3133 else
3134 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not exist in item.dbc! (not correct id?).", entry);
3135
3136 if (itemTemplate.Class >= MAX_ITEM_CLASS)
3137 {
3138 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Class value ({})", entry, itemTemplate.Class);
3139 itemTemplate.Class = ITEM_CLASS_MISCELLANEOUS;
3140 }
3141
3142 if (itemTemplate.SubClass >= MaxItemSubclassValues[itemTemplate.Class])
3143 {
3144 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Subclass value ({}) for class {}", entry, itemTemplate.SubClass, itemTemplate.Class);
3145 itemTemplate.SubClass = 0;// exist for all item classes
3146 }
3147
3148 if (itemTemplate.Quality >= MAX_ITEM_QUALITY)
3149 {
3150 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Quality value ({})", entry, itemTemplate.Quality);
3151 itemTemplate.Quality = ITEM_QUALITY_NORMAL;
3152 }
3153
3154 if (itemTemplate.HasFlag(ITEM_FLAG2_FACTION_HORDE))
3155 {
3156 if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE))
3157 if ((itemTemplate.AllowableRace & faction->ReputationRaceMask[0]) == 0)
3158 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.",
3159 entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_HORDE);
3160
3161 if (itemTemplate.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
3162 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.",
3164 }
3165 else if (itemTemplate.HasFlag(ITEM_FLAG2_FACTION_ALLIANCE))
3166 {
3167 if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE))
3168 if ((itemTemplate.AllowableRace & faction->ReputationRaceMask[0]) == 0)
3169 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.",
3170 entry, itemTemplate.AllowableRace, ITEM_FLAG2_FACTION_ALLIANCE);
3171 }
3172
3173 if (itemTemplate.BuyCount <= 0)
3174 {
3175 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong BuyCount value ({}), set to default(1).", entry, itemTemplate.BuyCount);
3176 itemTemplate.BuyCount = 1;
3177 }
3178
3179 if (itemTemplate.InventoryType >= MAX_INVTYPE)
3180 {
3181 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong InventoryType value ({})", entry, itemTemplate.InventoryType);
3182 itemTemplate.InventoryType = INVTYPE_NON_EQUIP;
3183 }
3184
3185 if (itemTemplate.RequiredSkill >= MAX_SKILL_TYPE)
3186 {
3187 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong RequiredSkill value ({})", entry, itemTemplate.RequiredSkill);
3188 itemTemplate.RequiredSkill = 0;
3189 }
3190
3191 {
3192 // can be used in equip slot, as page read use in inventory, or spell casting at use
3193 bool req = itemTemplate.InventoryType != INVTYPE_NON_EQUIP || itemTemplate.PageText;
3194 if (!req)
3195 for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
3196 {
3197 if (itemTemplate.Effects[j].SpellID > 0)
3198 {
3199 req = true;
3200 break;
3201 }
3202 }
3203
3204 if (req)
3205 {
3206 if (!(itemTemplate.AllowableClass & CLASSMASK_ALL_PLAYABLE))
3207 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);
3208
3209 if (!(itemTemplate.AllowableRace & RACEMASK_ALL_PLAYABLE))
3210 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);
3211 }
3212 }
3213
3214 if (itemTemplate.RequiredSpell && !sSpellMgr->GetSpellInfo(itemTemplate.RequiredSpell))
3215 {
3216 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has a wrong (non-existing) spell in RequiredSpell ({})", entry, itemTemplate.RequiredSpell);
3217 itemTemplate.RequiredSpell = 0;
3218 }
3219
3220 if (itemTemplate.RequiredReputationRank >= MAX_REPUTATION_RANK)
3221 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong reputation rank in RequiredReputationRank ({}), item can't be used.", entry, itemTemplate.RequiredReputationRank);
3222
3223 if (itemTemplate.RequiredReputationFaction)
3224 {
3225 if (!sFactionStore.LookupEntry(itemTemplate.RequiredReputationFaction))
3226 {
3227 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (not existing) faction in RequiredReputationFaction ({})", entry, itemTemplate.RequiredReputationFaction);
3228 itemTemplate.RequiredReputationFaction = 0;
3229 }
3230
3231 if (itemTemplate.RequiredReputationRank == MIN_REPUTATION_RANK)
3232 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", entry);
3233 }
3234
3235 if (itemTemplate.MaxCount < -1)
3236 {
3237 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large negative in maxcount ({}), replace by value (-1) no storing limits.", entry, itemTemplate.MaxCount);
3238 itemTemplate.MaxCount = -1;
3239 }
3240
3241 if (itemTemplate.Stackable == 0)
3242 {
3243 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong value in stackable ({}), replace by default 1.", entry, itemTemplate.Stackable);
3244 itemTemplate.Stackable = 1;
3245 }
3246 else if (itemTemplate.Stackable < -1)
3247 {
3248 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large negative in stackable ({}), replace by value (-1) no stacking limits.", entry, itemTemplate.Stackable);
3249 itemTemplate.Stackable = -1;
3250 }
3251
3252 if (itemTemplate.ContainerSlots > MAX_BAG_SIZE)
3253 {
3254 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has too large value in ContainerSlots ({}), replace by hardcoded limit ({}).", entry, itemTemplate.ContainerSlots, MAX_BAG_SIZE);
3255 itemTemplate.ContainerSlots = MAX_BAG_SIZE;
3256 }
3257
3258 for (uint8 j = 0; j < itemTemplate.StatsCount; ++j)
3259 {
3260 // for ItemStatValue != 0
3261 if (itemTemplate.ItemStat[j].ItemStatValue && itemTemplate.ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
3262 {
3263 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (non-existing?) stat_type{} ({})", entry, j+1, itemTemplate.ItemStat[j].ItemStatType);
3264 itemTemplate.ItemStat[j].ItemStatType = 0;
3265 }
3266
3267 switch (itemTemplate.ItemStat[j].ItemStatType)
3268 {
3271 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has deprecated stat_type{} ({})", entry, j+1, itemTemplate.ItemStat[j].ItemStatType);
3272 break;
3273 default:
3274 break;
3275 }
3276 }
3277
3278 for (uint8 j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j)
3279 {
3280 if (itemTemplate.Damage[j].DamageType >= MAX_SPELL_SCHOOL)
3281 {
3282 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong dmg_type{} ({})", entry, j+1, itemTemplate.Damage[j].DamageType);
3283 itemTemplate.Damage[j].DamageType = 0;
3284 }
3285 }
3286
3287 for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
3288 {
3289 if (itemTemplate.Effects[j].TriggerType >= MAX_ITEM_SPELLTRIGGER)
3290 {
3291 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong item spell trigger value in spelltrigger_{} ({})", entry, j+1, itemTemplate.Effects[j].TriggerType);
3292 itemTemplate.Effects[j].SpellID = 0;
3293 itemTemplate.Effects[j].TriggerType = ITEM_SPELLTRIGGER_ON_USE;
3294 }
3295
3296 if (itemTemplate.Effects[j].SpellID > 0)
3297 {
3298 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itemTemplate.Effects[j].SpellID);
3299 if (!spellInfo && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, itemTemplate.Effects[j].SpellID, nullptr))
3300 {
3301 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong (not existing) spell in spellid_{} ({})", entry, j+1, itemTemplate.Effects[j].SpellID);
3302 itemTemplate.Effects[j].SpellID = 0;
3303 }
3304 }
3305 }
3306
3307 if (itemTemplate.Bonding >= MAX_BIND_TYPE)
3308 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Bonding value ({})", entry, itemTemplate.Bonding);
3309
3310 if (itemTemplate.PageText && !GetPageText(itemTemplate.PageText))
3311 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has non existing first page (Id:{})", entry, itemTemplate.PageText);
3312
3313 if (itemTemplate.LockID && !sLockStore.LookupEntry(itemTemplate.LockID))
3314 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong LockID ({})", entry, itemTemplate.LockID);
3315
3316 if (itemTemplate.Sheath >= MAX_SHEATHETYPE)
3317 {
3318 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Sheath ({})", entry, itemTemplate.Sheath);
3319 itemTemplate.Sheath = SHEATHETYPE_NONE;
3320 }
3321
3322 if (itemTemplate.RandomProperty)
3323 {
3324 // To be implemented later
3325 if (itemTemplate.RandomProperty == -1)
3326 itemTemplate.RandomProperty = 0;
3327
3328 else if (!sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomProperty)))
3329 {
3330 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty ({})", entry, itemTemplate.RandomProperty);
3331 itemTemplate.RandomProperty = 0;
3332 }
3333 }
3334
3335 if (itemTemplate.RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(itemTemplate.RandomSuffix)))
3336 {
3337 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong RandomSuffix ({})", entry, itemTemplate.RandomSuffix);
3338 itemTemplate.RandomSuffix = 0;
3339 }
3340
3341 if (itemTemplate.ItemSet && !sItemSetStore.LookupEntry(itemTemplate.ItemSet))
3342 {
3343 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) have wrong ItemSet ({})", entry, itemTemplate.ItemSet);
3344 itemTemplate.ItemSet = 0;
3345 }
3346
3347 if (itemTemplate.Area && !sAreaTableStore.LookupEntry(itemTemplate.Area))
3348 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Area ({})", entry, itemTemplate.Area);
3349
3350 if (itemTemplate.Map && !sMapStore.LookupEntry(itemTemplate.Map))
3351 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Map ({})", entry, itemTemplate.Map);
3352
3353 if (itemTemplate.BagFamily)
3354 {
3355 // check bits
3356 for (uint32 j = 0; j < sizeof(itemTemplate.BagFamily)*8; ++j)
3357 {
3358 uint32 mask = 1 << j;
3359 if ((itemTemplate.BagFamily & mask) == 0)
3360 continue;
3361
3362 ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1);
3363 if (!bf)
3364 {
3365 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", entry);
3366 itemTemplate.BagFamily &= ~mask;
3367 continue;
3368 }
3369
3371 {
3372 CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemTemplate.ItemId);
3373 if (!ctEntry)
3374 {
3375 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit", entry);
3376 itemTemplate.BagFamily &= ~mask;
3377 }
3378 }
3379 }
3380 }
3381
3382 if (itemTemplate.TotemCategory && !sTotemCategoryStore.LookupEntry(itemTemplate.TotemCategory))
3383 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong TotemCategory ({})", entry, itemTemplate.TotemCategory);
3384
3385 for (uint8 j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
3386 {
3387 if (itemTemplate.Socket[j].Color && (itemTemplate.Socket[j].Color & SOCKET_COLOR_ALL) != itemTemplate.Socket[j].Color)
3388 {
3389 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong socketColor_{} ({})", entry, j+1, itemTemplate.Socket[j].Color);
3390 itemTemplate.Socket[j].Color = 0;
3391 }
3392 }
3393
3394 if (itemTemplate.GemProperties && !sGemPropertiesStore.LookupEntry(itemTemplate.GemProperties))
3395 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong GemProperties ({})", entry, itemTemplate.GemProperties);
3396
3397 if (itemTemplate.FoodType >= MAX_PET_DIET)
3398 {
3399 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong FoodType value ({})", entry, itemTemplate.FoodType);
3400 itemTemplate.FoodType = 0;
3401 }
3402
3403 if (itemTemplate.ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(itemTemplate.ItemLimitCategory))
3404 {
3405 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong LimitCategory value ({})", entry, itemTemplate.ItemLimitCategory);
3406 itemTemplate.ItemLimitCategory = 0;
3407 }
3408
3409 if (itemTemplate.HolidayId && !sHolidaysStore.LookupEntry(itemTemplate.HolidayId))
3410 {
3411 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong HolidayId value ({})", entry, itemTemplate.HolidayId);
3412 itemTemplate.HolidayId = 0;
3413 }
3414
3415 if (itemTemplate.FlagsCu & ITEM_FLAGS_CU_DURATION_REAL_TIME && !itemTemplate.Duration)
3416 {
3417 TC_LOG_ERROR("sql.sql", "Item (Entry {}) has flag ITEM_FLAGS_CU_DURATION_REAL_TIME but it does not have duration limit", entry);
3418 itemTemplate.FlagsCu &= ~ITEM_FLAGS_CU_DURATION_REAL_TIME;
3419 }
3420
3421 // Load cached data
3422 itemTemplate._LoadTotalAP();
3423 } while (result->NextRow());
3424
3425 // Check if item templates for DBC referenced character start outfit are present
3426 std::set<uint32> notFoundOutfit;
3427 for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
3428 {
3429 CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
3430 if (!entry)
3431 continue;
3432
3433 for (uint8 j = 0; j < MAX_OUTFIT_ITEMS; ++j)
3434 {
3435 if (entry->ItemID[j] <= 0)
3436 continue;
3437
3438 uint32 item_id = entry->ItemID[j];
3439
3440 if (!GetItemTemplate(item_id))
3441 notFoundOutfit.insert(item_id);
3442 }
3443 }
3444
3445 for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
3446 TC_LOG_ERROR("sql.sql", "Item (Entry: {}) does not exist in `item_template` but is referenced in `CharStartOutfit.dbc`", *itr);
3447
3448 TC_LOG_INFO("server.loading", ">> Loaded {} item templates in {} ms", _itemTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3449}
3450
3455
3457{
3458 uint32 oldMSTime = getMSTime();
3459
3460 _itemSetNameLocaleStore.clear(); // need for reload case
3461
3462 // 0 1 2
3463 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Name FROM item_set_names_locale");
3464 if (!result)
3465 return;
3466
3467 do
3468 {
3469 Field* fields = result->Fetch();
3470
3471 uint32 id = fields[0].GetUInt32();
3472 std::string localeName = fields[1].GetString();
3473
3474 LocaleConstant locale = GetLocaleByName(localeName);
3475 if (locale == LOCALE_enUS)
3476 continue;
3477
3479 AddLocaleString(fields[2].GetString(), locale, data.Name);
3480 } while (result->NextRow());
3481
3482 TC_LOG_INFO("server.loading", ">> Loaded {} Item set name locale strings in {} ms", uint64(_itemSetNameLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
3483}
3484
3486{
3487 uint32 oldMSTime = getMSTime();
3488
3489 _itemSetNameStore.clear(); // needed for reload case
3490
3491 std::set<uint32> itemSetItems;
3492
3493 // fill item set member ids
3494 for (uint32 entryId = 0; entryId < sItemSetStore.GetNumRows(); ++entryId)
3495 {
3496 ItemSetEntry const* setEntry = sItemSetStore.LookupEntry(entryId);
3497 if (!setEntry)
3498 continue;
3499
3500 for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i)
3501 if (setEntry->ItemID[i])
3502 itemSetItems.insert(setEntry->ItemID[i]);
3503 }
3504
3505 // 0 1 2
3506 QueryResult result = WorldDatabase.Query("SELECT `entry`, `name`, `InventoryType` FROM `item_set_names`");
3507
3508 if (!result)
3509 {
3510 TC_LOG_INFO("server.loading", ">> Loaded 0 item set names. DB table `item_set_names` is empty.");
3511 return;
3512 }
3513
3514 _itemSetNameStore.rehash(result->GetRowCount());
3515 uint32 count = 0;
3516
3517 do
3518 {
3519 Field* fields = result->Fetch();
3520
3521 uint32 entry = fields[0].GetUInt32();
3522 if (itemSetItems.find(entry) == itemSetItems.end())
3523 {
3524 TC_LOG_ERROR("sql.sql", "Item set name (Entry: {}) not found in ItemSet.dbc, data useless.", entry);
3525 continue;
3526 }
3527
3528 ItemSetNameEntry &data = _itemSetNameStore[entry];
3529 data.name = fields[1].GetString();
3530
3531 uint32 invType = fields[2].GetUInt8();
3532 if (invType >= MAX_INVTYPE)
3533 {
3534 TC_LOG_ERROR("sql.sql", "Item set name (Entry: {}) has wrong InventoryType value ({})", entry, invType);
3535 invType = INVTYPE_NON_EQUIP;
3536 }
3537
3538 data.InventoryType = invType;
3539 itemSetItems.erase(entry);
3540 ++count;
3541 } while (result->NextRow());
3542
3543 if (!itemSetItems.empty())
3544 {
3545 ItemTemplate const* pProto;
3546 for (std::set<uint32>::iterator itr = itemSetItems.begin(); itr != itemSetItems.end(); ++itr)
3547 {
3548 uint32 entry = *itr;
3549 // add data from item_template if available
3550 pProto = sObjectMgr->GetItemTemplate(entry);
3551 if (pProto)
3552 {
3553 TC_LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, adding data from `item_template`.", entry);
3554 ItemSetNameEntry &data = _itemSetNameStore[entry];
3555 data.name = pProto->Name1;
3556 data.InventoryType = pProto->InventoryType;
3557 ++count;
3558 }
3559 else
3560 TC_LOG_ERROR("sql.sql", "Item set part (Entry: {}) does not have entry in `item_set_names`, set will not display properly.", entry);
3561 }
3562 }
3563
3564 TC_LOG_INFO("server.loading", ">> Loaded {} item set names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3565}
3566
3568{
3569 uint32 oldMSTime = getMSTime();
3570
3571 _vehicleTemplateAccessoryStore.clear(); // needed for reload case
3572
3573 uint32 count = 0;
3574
3575 // 0 1 2 3 4 5
3576 QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_template_accessory`");
3577
3578 if (!result)
3579 {
3580 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template accessories. DB table `vehicle_template_accessory` is empty.");
3581 return;
3582 }
3583
3584 do
3585 {
3586 Field* fields = result->Fetch();
3587
3588 uint32 entry = fields[0].GetUInt32();
3589 uint32 accessory = fields[1].GetUInt32();
3590 int8 seatId = fields[2].GetInt8();
3591 bool isMinion = fields[3].GetBool();
3592 uint8 summonType = fields[4].GetUInt8();
3593 uint32 summonTimer = fields[5].GetUInt32();
3594
3595 if (!sObjectMgr->GetCreatureTemplate(entry))
3596 {
3597 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} does not exist.", entry);
3598 continue;
3599 }
3600
3601 if (!sObjectMgr->GetCreatureTemplate(accessory))
3602 {
3603 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory {} does not exist.", accessory);
3604 continue;
3605 }
3606
3607 if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end())
3608 {
3609 TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry {} has no data in npc_spellclick_spells", entry);
3610 continue;
3611 }
3612
3613 _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
3614
3615 ++count;
3616 }
3617 while (result->NextRow());
3618
3619 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3620}
3621
3623{
3624 uint32 oldMSTime = getMSTime();
3625
3626 _vehicleTemplateStore.clear();
3627
3628 // 0 1
3629 QueryResult result = WorldDatabase.Query("SELECT creatureId, despawnDelayMs FROM vehicle_template");
3630
3631 if (!result)
3632 {
3633 TC_LOG_INFO("server.loading", ">> Loaded 0 vehicle template. DB table `vehicle_template` is empty.");
3634 return;
3635 }
3636
3637 do
3638 {
3639 Field* fields = result->Fetch();
3640
3641 uint32 creatureId = fields[0].GetUInt32();
3642
3643 if (!sObjectMgr->GetCreatureTemplate(creatureId))
3644 {
3645 TC_LOG_ERROR("sql.sql", "Table `vehicle_template`: Vehicle {} does not exist.", creatureId);
3646 continue;
3647 }
3648
3649 VehicleTemplate& vehicleTemplate = _vehicleTemplateStore[creatureId];
3650 vehicleTemplate.DespawnDelay = Milliseconds(fields[1].GetInt32());
3651
3652 } while (result->NextRow());
3653
3654 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Template entries in {} ms", _vehicleTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
3655}
3656
3658{
3659 uint32 oldMSTime = getMSTime();
3660
3661 _vehicleAccessoryStore.clear(); // needed for reload case
3662
3663 uint32 count = 0;
3664
3665 // 0 1 2 3 4 5
3666 QueryResult result = WorldDatabase.Query("SELECT `guid`, `accessory_entry`, `seat_id`, `minion`, `summontype`, `summontimer` FROM `vehicle_accessory`");
3667
3668 if (!result)
3669 {
3670 TC_LOG_INFO("server.loading", ">> Loaded 0 Vehicle Accessories in {} ms", GetMSTimeDiffToNow(oldMSTime));
3671 return;
3672 }
3673
3674 do
3675 {
3676 Field* fields = result->Fetch();
3677
3678 ObjectGuid::LowType uiGUID = fields[0].GetUInt32();
3679 uint32 uiAccessory = fields[1].GetUInt32();
3680 int8 uiSeat = int8(fields[2].GetInt16());
3681 bool bMinion = fields[3].GetBool();
3682 uint8 uiSummonType = fields[4].GetUInt8();
3683 uint32 uiSummonTimer= fields[5].GetUInt32();
3684
3685 if (!sObjectMgr->GetCreatureTemplate(uiAccessory))
3686 {
3687 TC_LOG_ERROR("sql.sql", "Table `vehicle_accessory`: Accessory {} does not exist.", uiAccessory);
3688 continue;
3689 }
3690
3691 _vehicleAccessoryStore[uiGUID].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer));
3692
3693 ++count;
3694 }
3695 while (result->NextRow());
3696
3697 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Accessories in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3698}
3699
3701{
3702 uint32 oldMSTime = getMSTime();
3703
3704 _vehicleSeatAddonStore.clear(); // needed for reload case
3705
3706 uint32 count = 0;
3707
3708 // 0 1 2 3 4 5 6
3709 QueryResult result = WorldDatabase.Query("SELECT `SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue` FROM `vehicle_seat_addon`");
3710
3711 if (!result)
3712 {
3713 TC_LOG_ERROR("server.loading", ">> Loaded 0 vehicle seat addons. DB table `vehicle_seat_addon` is empty.");
3714 return;
3715 }
3716
3717 do
3718 {
3719 Field* fields = result->Fetch();
3720
3721 uint32 seatID = fields[0].GetUInt32();
3722 float orientation = fields[1].GetFloat();
3723 float exitX = fields[2].GetFloat();
3724 float exitY = fields[3].GetFloat();
3725 float exitZ = fields[4].GetFloat();
3726 float exitO = fields[5].GetFloat();
3727 uint8 exitParam = fields[6].GetUInt8();
3728
3729 if (!sVehicleSeatStore.LookupEntry(seatID))
3730 {
3731 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} does not exist in VehicleSeat.dbc. Skipping entry.", seatID);
3732 continue;
3733 }
3734
3735 // Sanitizing values
3736 if (orientation > float(M_PI * 2))
3737 {
3738 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid angle offset value ({}). Set Value to 0.", seatID, orientation);
3739 orientation = 0.0f;
3740 }
3741
3743 {
3744 TC_LOG_ERROR("sql.sql", "Table `vehicle_seat_addon`: SeatID: {} is using invalid exit parameter value ({}). Setting to 0 (none).", seatID, exitParam);
3745 continue;
3746 }
3747
3748 _vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, exitX, exitY, exitZ, exitO, exitParam);
3749
3750 ++count;
3751 } while (result->NextRow());
3752
3753 TC_LOG_INFO("server.loading", ">> Loaded {} Vehicle Seat Addon entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3754}
3755
3757{
3758 uint32 oldMSTime = getMSTime();
3759
3760 // 0 1 2 3 4 5 6 7 8 9 10 11
3761 QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor, min_dmg, max_dmg FROM pet_levelstats");
3762
3763 if (!result)
3764 {
3765 TC_LOG_INFO("server.loading", ">> Loaded 0 level pet stats definitions. DB table `pet_levelstats` is empty.");
3766 return;
3767 }
3768
3769 uint32 count = 0;
3770
3771 do
3772 {
3773 Field* fields = result->Fetch();
3774
3775 uint32 creature_id = fields[0].GetUInt32();
3776 if (!sObjectMgr->GetCreatureTemplate(creature_id))
3777 {
3778 TC_LOG_ERROR("sql.sql", "Wrong creature id {} in `pet_levelstats` table, ignoring.", creature_id);
3779 continue;
3780 }
3781
3782 uint32 current_level = fields[1].GetUInt8();
3783 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3784 {
3785 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
3786 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
3787 else
3788 {
3789 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `pet_levelstats` table, ignoring.", current_level);
3790 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
3791 }
3792 continue;
3793 }
3794 else if (current_level < 1)
3795 {
3796 TC_LOG_ERROR("sql.sql", "Wrong (<1) level {} in `pet_levelstats` table, ignoring.", current_level);
3797 continue;
3798 }
3799
3800 auto& pInfoMapEntry = _petInfoStore[creature_id];
3801 if (!pInfoMapEntry)
3802 pInfoMapEntry = std::make_unique<PetLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
3803
3804 // data for level 1 stored in [0] array element, ...
3805 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
3806
3807 pLevelInfo->health = fields[2].GetUInt16();
3808 pLevelInfo->mana = fields[3].GetUInt16();
3809 pLevelInfo->armor = fields[9].GetUInt32();
3810 pLevelInfo->minDamage = fields[10].GetUInt16();
3811 pLevelInfo->maxDamage = fields[11].GetUInt16();
3812
3813 for (uint8 i = 0; i < MAX_STATS; i++)
3814 pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
3815
3816 ++count;
3817 }
3818 while (result->NextRow());
3819
3820 // Fill gaps and check integrity
3821 for (PetLevelInfoContainer::iterator itr = _petInfoStore.begin(); itr != _petInfoStore.end(); ++itr)
3822 {
3823 auto& pInfo = itr->second;
3824
3825 // fatal error if no level 1 data
3826 if (!pInfo || pInfo[0].health == 0)
3827 {
3828 TC_LOG_ERROR("sql.sql", "Creature {} does not have pet stats data for Level 1!", itr->first);
3829 ABORT();
3830 }
3831
3832 // fill level gaps
3833 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
3834 {
3835 if (pInfo[level].health == 0)
3836 {
3837 TC_LOG_ERROR("sql.sql", "Creature {} has no data for Level {} pet stats data, using data of Level {}.", itr->first, level + 1, level);
3838 pInfo[level] = pInfo[level - 1];
3839 }
3840 }
3841 }
3842
3843 TC_LOG_INFO("server.loading", ">> Loaded {} level pet stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3844}
3845
3846PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint8 level) const
3847{
3848 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
3849 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
3850
3851 auto itr = _petInfoStore.find(creature_id);
3852 if (itr == _petInfoStore.end())
3853 return nullptr;
3854
3855 return &itr->second[level - 1]; // data for level 1 stored in [0] array element, ...
3856}
3857
3859{
3860 if (!_playerInfo[race_][class_])
3861 return;
3862
3863 if (count > 0)
3864 _playerInfo[race_][class_]->item.push_back(PlayerCreateInfoItem(itemId, count));
3865 else
3866 {
3867 if (count < -1)
3868 TC_LOG_ERROR("sql.sql", "Invalid count {} specified on item {} be removed from original player create info (use -1)!", count, itemId);
3869
3870 for (uint32 gender = 0; gender < GENDER_NONE; ++gender)
3871 {
3872 if (CharStartOutfitEntry const* entry = GetCharStartOutfitEntry(race_, class_, gender))
3873 {
3874 bool found = false;
3875 for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
3876 {
3877 if (entry->ItemID[x] > 0 && uint32(entry->ItemID[x]) == itemId)
3878 {
3879 found = true;
3880 const_cast<CharStartOutfitEntry*>(entry)->ItemID[x] = 0;
3881 break;
3882 }
3883 }
3884
3885 if (!found)
3886 TC_LOG_ERROR("sql.sql", "Item {} specified to be removed from original create info not found in dbc!", itemId);
3887 }
3888 }
3889 }
3890}
3891
3893{
3894 // Load playercreate
3895 {
3896 uint32 oldMSTime = getMSTime();
3897 // 0 1 2 3 4 5 6
3898 QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");
3899
3900 if (!result)
3901 {
3902 TC_LOG_ERROR("server.loading", ">> Loaded 0 player create definitions. DB table `playercreateinfo` is empty.");
3903 ABORT();
3904 }
3905 else
3906 {
3907 uint32 count = 0;
3908
3909 do
3910 {
3911 Field* fields = result->Fetch();
3912
3913 uint32 current_race = fields[0].GetUInt8();
3914 uint32 current_class = fields[1].GetUInt8();
3915 uint32 mapId = fields[2].GetUInt16();
3916 uint32 areaId = fields[3].GetUInt32(); // zone
3917 float positionX = fields[4].GetFloat();
3918 float positionY = fields[5].GetFloat();
3919 float positionZ = fields[6].GetFloat();
3920 float orientation = fields[7].GetFloat();
3921
3922 if (current_race >= MAX_RACES)
3923 {
3924 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3925 continue;
3926 }
3927
3928 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
3929 if (!rEntry)
3930 {
3931 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo` table, ignoring.", current_race);
3932 continue;
3933 }
3934
3935 if (current_class >= MAX_CLASSES)
3936 {
3937 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3938 continue;
3939 }
3940
3941 if (!sChrClassesStore.LookupEntry(current_class))
3942 {
3943 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo` table, ignoring.", current_class);
3944 continue;
3945 }
3946
3947 // accept DB data only for valid position (and non instanceable)
3948 if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
3949 {
3950 TC_LOG_ERROR("sql.sql", "Wrong home position for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3951 continue;
3952 }
3953
3954 if (sMapStore.LookupEntry(mapId)->Instanceable())
3955 {
3956 TC_LOG_ERROR("sql.sql", "Home position in instanceable map for class {} race {} pair in `playercreateinfo` table, ignoring.", current_class, current_race);
3957 continue;
3958 }
3959
3960 std::unique_ptr<PlayerInfo> info = std::make_unique<PlayerInfo>();
3961 info->mapId = mapId;
3962 info->areaId = areaId;
3963 info->positionX = positionX;
3964 info->positionY = positionY;
3965 info->positionZ = positionZ;
3966 info->orientation = orientation;
3967 info->displayId_m = rEntry->MaleDisplayID;
3968 info->displayId_f = rEntry->FemaleDisplayID;
3969 _playerInfo[current_race][current_class] = std::move(info);
3970
3971 ++count;
3972 }
3973 while (result->NextRow());
3974
3975 TC_LOG_INFO("server.loading", ">> Loaded {} player create definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
3976 }
3977 }
3978
3979 // Load playercreate items
3980 TC_LOG_INFO("server.loading", "Loading Player Create Items Data...");
3981 {
3982 uint32 oldMSTime = getMSTime();
3983 // 0 1 2 3
3984 QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
3985
3986 if (!result)
3987 {
3988 TC_LOG_INFO("server.loading", ">> Loaded 0 custom player create items. DB table `playercreateinfo_item` is empty.");
3989 }
3990 else
3991 {
3992 uint32 count = 0;
3993
3994 do
3995 {
3996 Field* fields = result->Fetch();
3997
3998 uint32 current_race = fields[0].GetUInt8();
3999 if (current_race >= MAX_RACES)
4000 {
4001 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_item` table, ignoring.", current_race);
4002 continue;
4003 }
4004
4005 uint32 current_class = fields[1].GetUInt8();
4006 if (current_class >= MAX_CLASSES)
4007 {
4008 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_item` table, ignoring.", current_class);
4009 continue;
4010 }
4011
4012 uint32 item_id = fields[2].GetUInt32();
4013
4014 if (!GetItemTemplate(item_id))
4015 {
4016 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);
4017 continue;
4018 }
4019
4020 int32 amount = fields[3].GetInt8();
4021
4022 if (!amount)
4023 {
4024 TC_LOG_ERROR("sql.sql", "Item id {} (class {} race {}) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
4025 continue;
4026 }
4027
4028 if (!current_race || !current_class)
4029 {
4030 uint32 min_race = current_race ? current_race : 1;
4031 uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
4032 uint32 min_class = current_class ? current_class : 1;
4033 uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
4034 for (uint32 r = min_race; r < max_race; ++r)
4035 for (uint32 c = min_class; c < max_class; ++c)
4036 PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
4037 }
4038 else
4039 PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);
4040
4041 ++count;
4042 }
4043 while (result->NextRow());
4044
4045 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4046 }
4047 }
4048
4049 // Load playercreate skills
4050 TC_LOG_INFO("server.loading", "Loading Player Create Skill Data...");
4051 {
4052 uint32 oldMSTime = getMSTime();
4053
4054 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, skill, `rank` FROM playercreateinfo_skills");
4055
4056 if (!result)
4057 {
4058 TC_LOG_INFO("server.loading", ">> Loaded 0 player create skills. DB table `playercreateinfo_skills` is empty.");
4059 }
4060 else
4061 {
4062 uint32 count = 0;
4063
4064 do
4065 {
4066 Field* fields = result->Fetch();
4067 uint32 raceMask = fields[0].GetUInt32();
4068 uint32 classMask = fields[1].GetUInt32();
4070 skill.SkillId = fields[2].GetUInt16();
4071 skill.Rank = fields[3].GetUInt16();
4072
4073 if (skill.Rank >= MAX_SKILL_STEP)
4074 {
4075 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);
4076 continue;
4077 }
4078
4079 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
4080 {
4081 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_skills` table, ignoring.", raceMask);
4082 continue;
4083 }
4084
4085 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4086 {
4087 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_skills` table, ignoring.", classMask);
4088 continue;
4089 }
4090
4091 if (!sSkillLineStore.LookupEntry(skill.SkillId))
4092 {
4093 TC_LOG_ERROR("sql.sql", "Wrong skill id {} in `playercreateinfo_skills` table, ignoring.", skill.SkillId);
4094 continue;
4095 }
4096
4097 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4098 {
4099 if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
4100 {
4101 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4102 {
4103 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4104 {
4105 if (!GetSkillRaceClassInfo(skill.SkillId, raceIndex, classIndex))
4106 continue;
4107
4108 if (auto& info = _playerInfo[raceIndex][classIndex])
4109 {
4110 info->skills.push_back(skill);
4111 ++count;
4112 }
4113 }
4114 }
4115 }
4116 }
4117 } while (result->NextRow());
4118
4119 TC_LOG_INFO("server.loading", ">> Loaded {} player create skills in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4120 }
4121 }
4122
4123 // Load playercreate spells
4124 TC_LOG_INFO("server.loading", "Loading Player Create Spell Data...");
4125 {
4126 uint32 oldMSTime = getMSTime();
4127
4128 QueryResult result = WorldDatabase.PQuery("SELECT racemask, classmask, Spell FROM playercreateinfo_spell_custom");
4129
4130 if (!result)
4131 {
4132 TC_LOG_INFO("server.loading", ">> Loaded 0 player create spells. DB table `playercreateinfo_spell_custom` is empty.");
4133 }
4134 else
4135 {
4136 uint32 count = 0;
4137
4138 do
4139 {
4140 Field* fields = result->Fetch();
4141 uint32 raceMask = fields[0].GetUInt32();
4142 uint32 classMask = fields[1].GetUInt32();
4143 uint32 spellId = fields[2].GetUInt32();
4144
4145 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
4146 {
4147 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_spell_custom` table, ignoring.", raceMask);
4148 continue;
4149 }
4150
4151 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4152 {
4153 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_spell_custom` table, ignoring.", classMask);
4154 continue;
4155 }
4156
4157 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4158 {
4159 if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
4160 {
4161 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4162 {
4163 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4164 {
4165 if (auto& info = _playerInfo[raceIndex][classIndex])
4166 {
4167 info->customSpells.push_back(spellId);
4168 ++count;
4169 }
4170 // We need something better here, the check is not accounting for spells used by multiple races/classes but not all of them.
4171 // Either split the masks per class, or per race, which kind of kills the point yet.
4172 // else if (raceMask != 0 && classMask != 0)
4173 // 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);
4174 }
4175 }
4176 }
4177 }
4178 }
4179 while (result->NextRow());
4180
4181 TC_LOG_INFO("server.loading", ">> Loaded {} custom player create spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4182 }
4183 }
4184
4185 // Load playercreate cast spell
4186 TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data...");
4187 {
4188 uint32 oldMSTime = getMSTime();
4189
4190 QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell");
4191
4192 if (!result)
4193 TC_LOG_INFO("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty.");
4194 else
4195 {
4196 uint32 count = 0;
4197
4198 do
4199 {
4200 Field* fields = result->Fetch();
4201 uint32 raceMask = fields[0].GetUInt32();
4202 uint32 classMask = fields[1].GetUInt32();
4203 uint32 spellId = fields[2].GetUInt32();
4204
4205 if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE))
4206 {
4207 TC_LOG_ERROR("sql.sql", "Wrong race mask {} in `playercreateinfo_cast_spell` table, ignoring.", raceMask);
4208 continue;
4209 }
4210
4211 if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE))
4212 {
4213 TC_LOG_ERROR("sql.sql", "Wrong class mask {} in `playercreateinfo_cast_spell` table, ignoring.", classMask);
4214 continue;
4215 }
4216
4217 for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex)
4218 {
4219 if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask))
4220 {
4221 for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex)
4222 {
4223 if (classMask == 0 || ((1 << (classIndex - 1)) & classMask))
4224 {
4225 if (auto& info = _playerInfo[raceIndex][classIndex])
4226 {
4227 info->castSpells.push_back(spellId);
4228 ++count;
4229 }
4230 }
4231 }
4232 }
4233 }
4234 } while (result->NextRow());
4235
4236 TC_LOG_INFO("server.loading", ">> Loaded {} player create cast spells in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4237 }
4238 }
4239
4240 // Load playercreate actions
4241 TC_LOG_INFO("server.loading", "Loading Player Create Action Data...");
4242 {
4243 uint32 oldMSTime = getMSTime();
4244
4245 // 0 1 2 3 4
4246 QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
4247
4248 if (!result)
4249 {
4250 TC_LOG_INFO("server.loading", ">> Loaded 0 player create actions. DB table `playercreateinfo_action` is empty.");
4251 }
4252 else
4253 {
4254 uint32 count = 0;
4255
4256 do
4257 {
4258 Field* fields = result->Fetch();
4259
4260 uint32 current_race = fields[0].GetUInt8();
4261 if (current_race >= MAX_RACES)
4262 {
4263 TC_LOG_ERROR("sql.sql", "Wrong race {} in `playercreateinfo_action` table, ignoring.", current_race);
4264 continue;
4265 }
4266
4267 uint32 current_class = fields[1].GetUInt8();
4268 if (current_class >= MAX_CLASSES)
4269 {
4270 TC_LOG_ERROR("sql.sql", "Wrong class {} in `playercreateinfo_action` table, ignoring.", current_class);
4271 continue;
4272 }
4273
4274 if (auto& info = _playerInfo[current_race][current_class])
4275 info->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt16(), fields[3].GetUInt32(), fields[4].GetUInt16()));
4276
4277 ++count;
4278 }
4279 while (result->NextRow());
4280
4281 TC_LOG_INFO("server.loading", ">> Loaded {} player create actions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4282 }
4283 }
4284
4285 // Loading levels data (class only dependent)
4286 TC_LOG_INFO("server.loading", "Loading Player Create Level HP/Mana Data...");
4287 {
4288 uint32 oldMSTime = getMSTime();
4289
4290 // 0 1 2 3
4291 QueryResult result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
4292
4293 if (!result)
4294 {
4295 TC_LOG_ERROR("server.loading", ">> Loaded 0 level health/mana definitions. DB table `player_classlevelstats` is empty.");
4296 ABORT();
4297 }
4298
4299 uint32 count = 0;
4300
4301 do
4302 {
4303 Field* fields = result->Fetch();
4304
4305 uint32 current_class = fields[0].GetUInt8();
4306 if (current_class >= MAX_CLASSES)
4307 {
4308 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_classlevelstats` table, ignoring.", current_class);
4309 continue;
4310 }
4311
4312 uint8 current_level = fields[1].GetUInt8(); // Can't be > than STRONG_MAX_LEVEL (hardcoded level maximum) due to var type
4313 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4314 {
4315 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_classlevelstats` table, ignoring.", current_level);
4316 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4317 continue;
4318 }
4319
4320 auto& info = _playerClassInfo[current_class];
4321 if (!info)
4322 {
4323 info = std::make_unique<PlayerClassInfo>();
4324 info->levelInfo = std::make_unique<PlayerClassLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4325 }
4326
4327 PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level - 1];
4328 levelInfo.basehealth = fields[2].GetUInt16();
4329 levelInfo.basemana = fields[3].GetUInt16();
4330
4331 ++count;
4332 }
4333 while (result->NextRow());
4334
4335 // Fill gaps and check integrity
4336 for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
4337 {
4338 // skip non existed classes
4339 if (!sChrClassesStore.LookupEntry(class_))
4340 continue;
4341
4342 auto& pClassInfo = _playerClassInfo[class_];
4343
4344 // fatal error if no level 1 data
4345 if (!pClassInfo || !pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0)
4346 {
4347 TC_LOG_ERROR("sql.sql", "Class {} Level 1 does not have health/mana data!", class_);
4348 ABORT();
4349 }
4350
4351 // fill level gaps
4352 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4353 {
4354 if (pClassInfo->levelInfo[level].basehealth == 0)
4355 {
4356 TC_LOG_ERROR("sql.sql", "Class {} Level {} does not have health/mana data. Using stats data of level {}.", class_, level + 1, level);
4357 pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
4358 }
4359 }
4360 }
4361
4362 TC_LOG_INFO("server.loading", ">> Loaded {} level health/mana definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4363 }
4364
4365 // Loading levels data (class/race dependent)
4366 TC_LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
4367 {
4368 uint32 oldMSTime = getMSTime();
4369
4370 // 0 1 2 3 4 5 6 7
4371 QueryResult result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
4372
4373 if (!result)
4374 {
4375 TC_LOG_ERROR("server.loading", ">> Loaded 0 level stats definitions. DB table `player_levelstats` is empty.");
4376 ABORT();
4377 }
4378
4379 uint32 count = 0;
4380
4381 do
4382 {
4383 Field* fields = result->Fetch();
4384
4385 uint32 current_race = fields[0].GetUInt8();
4386 if (current_race >= MAX_RACES)
4387 {
4388 TC_LOG_ERROR("sql.sql", "Wrong race {} in `player_levelstats` table, ignoring.", current_race);
4389 continue;
4390 }
4391
4392 uint32 current_class = fields[1].GetUInt8();
4393 if (current_class >= MAX_CLASSES)
4394 {
4395 TC_LOG_ERROR("sql.sql", "Wrong class {} in `player_levelstats` table, ignoring.", current_class);
4396 continue;
4397 }
4398
4399 uint32 current_level = fields[2].GetUInt8();
4400 if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4401 {
4402 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4403 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4404 else
4405 {
4406 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_levelstats` table, ignoring.", current_level);
4407 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4408 }
4409 continue;
4410 }
4411
4412 if (auto& info = _playerInfo[current_race][current_class])
4413 {
4414 if (!info->levelInfo)
4415 info->levelInfo = std::make_unique<PlayerLevelInfo[]>(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4416
4417 PlayerLevelInfo& levelInfo = info->levelInfo[current_level - 1];
4418 for (uint8 i = 0; i < MAX_STATS; ++i)
4419 levelInfo.stats[i] = fields[i + 3].GetUInt8();
4420 }
4421
4422 ++count;
4423 }
4424 while (result->NextRow());
4425
4426 // Fill gaps and check integrity
4427 for (uint8 race = 0; race < MAX_RACES; ++race)
4428 {
4429 // skip non existed races
4430 if (!sChrRacesStore.LookupEntry(race))
4431 continue;
4432
4433 for (uint8 class_ = 0; class_ < MAX_CLASSES; ++class_)
4434 {
4435 // skip non existed classes
4436 if (!sChrClassesStore.LookupEntry(class_))
4437 continue;
4438
4439 auto& info = _playerInfo[race][class_];
4440 if (!info)
4441 continue;
4442
4443 // skip expansion races if not playing with expansion
4444 if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_THE_BURNING_CRUSADE && (race == RACE_BLOODELF || race == RACE_DRAENEI))
4445 continue;
4446
4447 // skip expansion classes if not playing with expansion
4449 continue;
4450
4451 // fatal error if no level 1 data
4452 if (!info->levelInfo || info->levelInfo[0].stats[0] == 0)
4453 {
4454 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level 1 does not have stats data!", race, class_);
4455 ABORT();
4456 }
4457
4458 // fill level gaps
4459 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4460 {
4461 if (info->levelInfo[level].stats[0] == 0)
4462 {
4463 TC_LOG_ERROR("sql.sql", "Race {} Class {} Level {} does not have stats data. Using stats data of level {}.", race, class_, level + 1, level);
4464 info->levelInfo[level] = info->levelInfo[level - 1];
4465 }
4466 }
4467 }
4468 }
4469
4470 TC_LOG_INFO("server.loading", ">> Loaded {} level stats definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4471 }
4472
4473 // Loading xp per level data
4474 TC_LOG_INFO("server.loading", "Loading Player Create XP Data...");
4475 {
4476 uint32 oldMSTime = getMSTime();
4477
4478 _playerXPperLevel.resize(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
4479 for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4480 _playerXPperLevel[level] = 0;
4481
4482 // 0 1
4483 QueryResult result = WorldDatabase.Query("SELECT Level, Experience FROM player_xp_for_level");
4484
4485 if (!result)
4486 {
4487 TC_LOG_ERROR("server.loading", ">> Loaded 0 xp for level definitions. DB table `player_xp_for_level` is empty.");
4488 ABORT();
4489 }
4490
4491 uint32 count = 0;
4492
4493 do
4494 {
4495 Field* fields = result->Fetch();
4496
4497 uint32 current_level = fields[0].GetUInt8();
4498 uint32 current_xp = fields[1].GetUInt32();
4499
4500 if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4501 {
4502 if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
4503 TC_LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
4504 else
4505 {
4506 TC_LOG_INFO("misc", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_xp_for_levels` table, ignoring.", current_level);
4507 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
4508 }
4509 continue;
4510 }
4511 //PlayerXPperLevel
4512 _playerXPperLevel[current_level] = current_xp;
4513 ++count;
4514 }
4515 while (result->NextRow());
4516
4517 // fill level gaps
4518 for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
4519 {
4520 if (_playerXPperLevel[level] == 0)
4521 {
4522 TC_LOG_ERROR("sql.sql", "Level {} does not have XP for level data. Using data of level [{}] + 100.", level + 1, level);
4523 _playerXPperLevel[level] = _playerXPperLevel[level - 1] + 100;
4524 }
4525 }
4526
4527 TC_LOG_INFO("server.loading", ">> Loaded {} xp for level definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
4528 }
4529}
4530
4532{
4533 if (level < 1 || class_ >= MAX_CLASSES)
4534 return;
4535
4536 auto const& pInfo = _playerClassInfo[class_];
4537
4538 if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4539 level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
4540
4541 *info = pInfo->levelInfo[level - 1];
4542}
4543
4545{
4546 if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
4547 return;
4548
4549 auto const& pInfo = _playerInfo[race][class_];
4550 if (!pInfo)
4551 return;
4552
4553 if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
4554 *info = pInfo->levelInfo[level - 1];
4555 else
4556 BuildPlayerLevelInfo(race, class_, level, info);
4557}
4558
4560{
4561 // base data (last known level)
4562 *info = _playerInfo[race][_class]->levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];
4563
4564 // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
4565 for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
4566 {
4567 switch (_class)
4568 {
4569 case CLASS_WARRIOR:
4570 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4571 info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
4572 info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4573 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4574 info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
4575 break;
4576 case CLASS_PALADIN:
4577 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
4578 info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4579 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
4580 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
4581 info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
4582 break;
4583 case CLASS_HUNTER:
4584 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
4585 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4586 info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
4587 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
4588 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
4589 break;
4590 case CLASS_ROGUE:
4591 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
4592 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4593 info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
4594 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
4595 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
4596 break;
4597 case CLASS_PRIEST:
4598 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4599 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4600 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
4601 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
4602 info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
4603 break;
4604 case CLASS_SHAMAN:
4605 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
4606 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
4607 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
4608 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
4609 info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
4610 break;
4611 case CLASS_MAGE:
4612 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4613 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
4614 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4615 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
4616 info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4617 break;
4618 case CLASS_WARLOCK:
4619 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
4620 info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4621 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
4622 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
4623 info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
4624 break;
4625 case CLASS_DRUID:
4626 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
4627 info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
4628 info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
4629 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
4630 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
4631 }
4632 }
4633}
4634
4636{
4637 uint32 oldMSTime = getMSTime();
4638
4639 _questTemplates.clear();
4640
4641 _exclusiveQuestGroups.clear();
4642
4643 QueryResult result = WorldDatabase.Query("SELECT "
4644 //0 1 2 3 4 5 6 7 8
4645 "ID, QuestType, QuestLevel, MinLevel, QuestSortID, QuestInfoID, SuggestedGroupNum, TimeAllowed, AllowableRaces,"
4646 // 9 10 11 12
4647 "RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, "
4648 // 13 14 15 16 17 18 19 20
4649 "RewardNextQuest, RewardXPDifficulty, RewardMoney, RewardBonusMoney, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
4650 // 21 22 23 24 25 26
4651 "StartItem, Flags, RewardTitle, RequiredPlayerKills, RewardTalents, RewardArenaPoints, "
4652 // 27 28 29 30 31 32 33 34
4653 "RewardItem1, RewardAmount1, RewardItem2, RewardAmount2, RewardItem3, RewardAmount3, RewardItem4, RewardAmount4, "
4654 // 35 36 37 38 39 40 41 42 43 44 45 46
4655 "RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemID6, RewardChoiceItemQuantity6, "
4656 // 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
4657 "RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionID5, RewardFactionValue5, RewardFactionOverride5,"
4658 // 62 63 64 65
4659 "POIContinent, POIx, POIy, POIPriority, "
4660 // 66 67 68 69 70
4661 "LogTitle, LogDescription, QuestDescription, AreaDescription, QuestCompletionLog, "
4662 // 71 72 73 74 75 76 77 78
4663 "RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
4664 // 79 80 81 82 83 84 85 86
4665 "ItemDrop1, ItemDrop2, ItemDrop3, ItemDrop4, ItemDropQuantity1, ItemDropQuantity2, ItemDropQuantity3, ItemDropQuantity4, "
4666 // 87 88 89 90 91 92 93 94 95 96 97 98
4667 "RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
4668 // 99 100 101 102 103
4669 "Unknown0, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4"
4670 " FROM quest_template");
4671 if (!result)
4672 {
4673 TC_LOG_INFO("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
4674 return;
4675 }
4676
4677 _questTemplates.reserve(result->GetRowCount());
4678
4679 // create multimap previous quest for each existed quest
4680 // some quests can have many previous maps set by NextQuestId in previous quest
4681 // for example set of race quests can lead to single not race specific quest
4682 do
4683 {
4684 Field* fields = result->Fetch();
4685
4686 uint32 questId = fields[0].GetUInt32();
4687 auto itr = _questTemplates.emplace(std::piecewise_construct, std::forward_as_tuple(questId), std::forward_as_tuple(new Quest(fields))).first;
4688 itr->second->_weakRef = itr->second;
4689 } while (result->NextRow());
4690
4691 std::unordered_map<uint32, uint32> usedMailTemplates;
4692
4693 struct QuestLoaderHelper
4694 {
4695 typedef void(Quest::*QuestLoaderFunction)(Field* fields);
4696
4697 char const* QueryFields;
4698 char const* TableName;
4699 char const* TableDesc;
4700 QuestLoaderFunction LoaderFunction;
4701 };
4702
4703 static std::vector<QuestLoaderHelper> const QuestLoaderHelpers =
4704 {
4705 // 0 1 2 3 4 5 6 7 8
4706 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4", "quest_details", "details", &Quest::LoadQuestDetails },
4707
4708 // 0 1 2 3
4709 { "ID, EmoteOnComplete, EmoteOnIncomplete, CompletionText", "quest_request_items", "request items", &Quest::LoadQuestRequestItems },
4710
4711 // 0 1 2 3 4 5 6 7 8 9
4712 { "ID, Emote1, Emote2, Emote3, Emote4, EmoteDelay1, EmoteDelay2, EmoteDelay3, EmoteDelay4, RewardText", "quest_offer_reward", "reward emotes", &Quest::LoadQuestOfferReward },
4713
4714 // 0 1 2 3 4 5 6 7 8 9
4715 { "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
4716 // 10 11 12 13 14 15 16 17
4717 " RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags", "quest_template_addon", "template addons", &Quest::LoadQuestTemplateAddon },
4718
4719 // 0 1
4720 { "QuestId, RewardMailSenderEntry", "quest_mail_sender", "mail sender entries", &Quest::LoadQuestMailSender }
4721 };
4722
4723 for (QuestLoaderHelper const& loader : QuestLoaderHelpers)
4724 {
4725 result = WorldDatabase.PQuery("SELECT {} FROM {}", loader.QueryFields, loader.TableName);
4726 if (!result)
4727 TC_LOG_INFO("server.loading", ">> Loaded 0 quest {}. DB table `{}` is empty.", loader.TableDesc, loader.TableName);
4728 else
4729 {
4730 do
4731 {
4732 Field* fields = result->Fetch();
4733 uint32 questId = fields[0].GetUInt32();
4734
4735 auto itr = _questTemplates.find(questId);
4736 if (itr != _questTemplates.end())
4737 (itr->second.get()->*loader.LoaderFunction)(fields);
4738 else
4739 TC_LOG_ERROR("server.loading", "Table `{}` has data for quest {} but such quest does not exist", loader.TableName, questId);
4740 } while (result->NextRow());
4741 }
4742 }
4743
4744 // Post processing
4745 for (auto& questPair : _questTemplates)
4746 {
4747 // skip post-loading checks for disabled quests
4748 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
4749 continue;
4750
4751 Quest* qinfo = questPair.second.get();
4752
4753 // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
4754
4755 if (qinfo->GetQuestMethod() >= 3)
4756 TC_LOG_ERROR("sql.sql", "Quest {} has `Method` = {}, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
4757
4759 {
4760 TC_LOG_ERROR("sql.sql", "Quest {} has `SpecialFlags` = {} > max allowed value. Correct `SpecialFlags` to value <= {}",
4763 }
4764
4765 if (qinfo->_flags & QUEST_FLAGS_DAILY && qinfo->_flags & QUEST_FLAGS_WEEKLY)
4766 {
4767 TC_LOG_ERROR("sql.sql", "Weekly Quest {} is marked as daily quest in `Flags`, removed daily flag.", qinfo->GetQuestId());
4768 qinfo->_flags &= ~QUEST_FLAGS_DAILY;
4769 }
4770
4771 if (qinfo->_flags & QUEST_FLAGS_DAILY)
4772 {
4774 {
4775 TC_LOG_ERROR("sql.sql", "Daily Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4777 }
4778 }
4779
4780 if (qinfo->_flags & QUEST_FLAGS_WEEKLY)
4781 {
4783 {
4784 TC_LOG_ERROR("sql.sql", "Weekly Quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4786 }
4787 }
4788
4790 {
4792 {
4793 TC_LOG_ERROR("sql.sql", "Monthly quest {} not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
4795 }
4796 }
4797
4798 if (qinfo->_flags & QUEST_FLAGS_TRACKING)
4799 {
4800 // at auto-reward can be rewarded only RewardChoiceItemId[0]
4801 for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
4802 {
4803 if (uint32 id = qinfo->RewardChoiceItemId[j])
4804 {
4805 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item from `RewardChoiceItemId{}` can't be rewarded with quest flag QUEST_FLAGS_TRACKING.",
4806 qinfo->GetQuestId(), j + 1, id, j + 1);
4807 // no changes, quest ignore this data
4808 }
4809 }
4810 }
4811
4812 // client quest log visual (area case)
4813 if (qinfo->_zoneOrSort > 0)
4814 {
4815 if (!sAreaTableStore.LookupEntry(qinfo->_zoneOrSort))
4816 {
4817 TC_LOG_ERROR("sql.sql", "Quest {} has `ZoneOrSort` = {} (zone case) but zone with this id does not exist.",
4818 qinfo->GetQuestId(), qinfo->_zoneOrSort);
4819 // no changes, quest not dependent from this value but can have problems at client
4820 }
4821 }
4822 // client quest log visual (sort case)
4823 if (qinfo->_zoneOrSort < 0)
4824 {
4825 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->_zoneOrSort));
4826 if (!qSort)
4827 {
4828 TC_LOG_ERROR("sql.sql", "Quest {} has `ZoneOrSort` = {} (sort case) but quest sort with this id does not exist.",
4829 qinfo->GetQuestId(), qinfo->_zoneOrSort);
4830 // 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)
4831 }
4832 //check for proper RequiredSkillId value (skill case)
4833 if (uint32 skill_id = SkillByQuestSort(-qinfo->_zoneOrSort))
4834 {
4835 if (qinfo->_requiredSkillId != skill_id)
4836 {
4837 TC_LOG_ERROR("sql.sql", "Quest {} has `ZoneOrSort` = {} but `RequiredSkillId` does not have a corresponding value ({}).",
4838 qinfo->GetQuestId(), qinfo->_zoneOrSort, skill_id);
4839 //override, and force proper value here?
4840 }
4841 }
4842 }
4843
4844 // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
4845 if (qinfo->_requiredClasses)
4846 {
4848 {
4849 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable classes in `RequiredClasses` ({}), value set to 0 (all classes).", qinfo->GetQuestId(), qinfo->_requiredClasses);
4850 qinfo->_requiredClasses = 0;
4851 }
4852 }
4853
4854 // AllowableRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
4855 if (qinfo->_allowableRaces)
4856 {
4857 if (!(qinfo->_allowableRaces & RACEMASK_ALL_PLAYABLE))
4858 {
4859 TC_LOG_ERROR("sql.sql", "Quest {} does not contain any playable races in `AllowableRaces` ({}), value set to 0 (all races).", qinfo->GetQuestId(), qinfo->_allowableRaces);
4860 qinfo->_allowableRaces = 0;
4861 }
4862 }
4863
4864 // RequiredSkillId, can be 0
4865 if (qinfo->_requiredSkillId)
4866 {
4867 if (!sSkillLineStore.LookupEntry(qinfo->_requiredSkillId))
4868 {
4869 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillId` = {} but this skill does not exist",
4870 qinfo->GetQuestId(), qinfo->_requiredSkillId);
4871 }
4872 }
4873
4874 if (qinfo->_requiredSkillPoints)
4875 {
4876 if (qinfo->_requiredSkillPoints > sWorld->GetConfigMaxSkillValue())
4877 {
4878 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredSkillPoints` = {} but max possible skill is {}, quest can't be done.",
4879 qinfo->GetQuestId(), qinfo->_requiredSkillPoints, sWorld->GetConfigMaxSkillValue());
4880 // no changes, quest can't be done for this requirement
4881 }
4882 }
4883 // else Skill quests can have 0 skill level, this is ok
4884
4885 if (qinfo->_requiredFactionId2 && !sFactionStore.LookupEntry(qinfo->_requiredFactionId2))
4886 {
4887 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionId2` = {} but faction template {} does not exist, quest can't be done.",
4888 qinfo->GetQuestId(), qinfo->_requiredFactionId2, qinfo->_requiredFactionId2);
4889 // no changes, quest can't be done for this requirement
4890 }
4891
4892 if (qinfo->_requiredFactionId1 && !sFactionStore.LookupEntry(qinfo->_requiredFactionId1))
4893 {
4894 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionId1` = {} but faction template {} does not exist, quest can't be done.",
4895 qinfo->GetQuestId(), qinfo->_requiredFactionId1, qinfo->_requiredFactionId1);
4896 // no changes, quest can't be done for this requirement
4897 }
4898
4899 if (qinfo->_requiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMinRepFaction))
4900 {
4901 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4903 // no changes, quest can't be done for this requirement
4904 }
4905
4906 if (qinfo->_requiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->_requiredMaxRepFaction))
4907 {
4908 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepFaction` = {} but faction template {} does not exist, quest can't be done.",
4910 // no changes, quest can't be done for this requirement
4911 }
4912
4914 {
4915 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but max reputation is {}, quest can't be done.",
4917 // no changes, quest can't be done for this requirement
4918 }
4919
4921 {
4922 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} and `RequiredMinRepValue` = {}, quest can't be done.",
4923 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue, qinfo->_requiredMinRepValue);
4924 // no changes, quest can't be done for this requirement
4925 }
4926
4927 if (!qinfo->_requiredFactionId1 && qinfo->_requiredFactionValue1 != 0)
4928 {
4929 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionValue1` = {} but `RequiredFactionId1` is 0, value has no effect",
4930 qinfo->GetQuestId(), qinfo->_requiredFactionValue1);
4931 // warning
4932 }
4933
4934 if (!qinfo->_requiredFactionId2 && qinfo->_requiredFactionValue2 != 0)
4935 {
4936 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredFactionValue2` = {} but `RequiredFactionId2` is 0, value has no effect",
4937 qinfo->GetQuestId(), qinfo->_requiredFactionValue2);
4938 // warning
4939 }
4940
4941 if (!qinfo->_requiredMinRepFaction && qinfo->_requiredMinRepValue != 0)
4942 {
4943 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMinRepValue` = {} but `RequiredMinRepFaction` is 0, value has no effect",
4944 qinfo->GetQuestId(), qinfo->_requiredMinRepValue);
4945 // warning
4946 }
4947
4948 if (!qinfo->_requiredMaxRepFaction && qinfo->_requiredMaxRepValue != 0)
4949 {
4950 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredMaxRepValue` = {} but `RequiredMaxRepFaction` is 0, value has no effect",
4951 qinfo->GetQuestId(), qinfo->_requiredMaxRepValue);
4952 // warning
4953 }
4954
4955 if (qinfo->_rewardTitleId && !sCharTitlesStore.LookupEntry(qinfo->_rewardTitleId))
4956 {
4957 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardTitleId` = {} but CharTitle Id {} does not exist, quest can't be rewarded with title.",
4958 qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
4959 qinfo->_rewardTitleId = 0;
4960 // quest can't reward this title
4961 }
4962
4963 if (qinfo->_startItem)
4964 {
4965 if (!sObjectMgr->GetItemTemplate(qinfo->_startItem))
4966 {
4967 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but item with entry {} does not exist, quest can't be done.",
4968 qinfo->GetQuestId(), qinfo->_startItem, qinfo->_startItem);
4969 qinfo->_startItem = 0; // quest can't be done for this requirement
4970 }
4971 else if (qinfo->_startItemCount == 0)
4972 {
4973 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = {} but `StartItemCount` = 0, set to 1 but need fix in DB.",
4974 qinfo->GetQuestId(), qinfo->_startItem);
4975 qinfo->_startItemCount = 1; // update to 1 for allow quest work for backward compatibility with DB
4976 }
4977 }
4978 else if (qinfo->_startItemCount > 0)
4979 {
4980 TC_LOG_ERROR("sql.sql", "Quest {} has `StartItem` = 0 but `StartItemCount` = {}, useless value.",
4981 qinfo->GetQuestId(), qinfo->_startItemCount);
4982 qinfo->_startItemCount = 0; // no quest work changes in fact
4983 }
4984
4985 if (qinfo->_sourceSpellid)
4986 {
4987 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_sourceSpellid);
4988 if (!spellInfo)
4989 {
4990 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} doesn't exist, quest can't be done.",
4991 qinfo->GetQuestId(), qinfo->_sourceSpellid, qinfo->_sourceSpellid);
4992 qinfo->_sourceSpellid = 0; // quest can't be done for this requirement
4993 }
4994 else if (!SpellMgr::IsSpellValid(spellInfo))
4995 {
4996 TC_LOG_ERROR("sql.sql", "Quest {} has `SourceSpellid` = {} but spell {} is broken, quest can't be done.",
4997 qinfo->GetQuestId(), qinfo->_sourceSpellid, qinfo->_sourceSpellid);
4998 qinfo->_sourceSpellid = 0; // quest can't be done for this requirement
4999 }
5000 }
5001
5002 for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
5003 {
5004 uint32 id = qinfo->RequiredItemId[j];
5005 if (id)
5006 {
5007 if (qinfo->RequiredItemCount[j] == 0)
5008 {
5009 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but `RequiredItemCount{}` = 0, quest can't be done.",
5010 qinfo->GetQuestId(), j+1, id, j+1);
5011 // no changes, quest can't be done for this requirement
5012 }
5013
5015
5016 if (!sObjectMgr->GetItemTemplate(id))
5017 {
5018 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = {} but item with entry {} does not exist, quest can't be done.",
5019 qinfo->GetQuestId(), j+1, id, id);
5020 qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest
5021 }
5022 }
5023 else if (qinfo->RequiredItemCount[j]>0)
5024 {
5025 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredItemId{}` = 0 but `RequiredItemCount{}` = {}, quest can't be done.",
5026 qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredItemCount[j]);
5027 qinfo->RequiredItemCount[j] = 0; // prevent incorrect work of quest
5028 }
5029 }
5030
5031 for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
5032 {
5033 uint32 id = qinfo->ItemDrop[j];
5034 if (id)
5035 {
5036 if (!sObjectMgr->GetItemTemplate(id))
5037 {
5038 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = {} but item with entry {} does not exist, quest can't be done.",
5039 qinfo->GetQuestId(), j+1, id, id);
5040 // no changes, quest can't be done for this requirement
5041 }
5042 }
5043 else
5044 {
5045 if (qinfo->ItemDropQuantity[j]>0)
5046 {
5047 TC_LOG_ERROR("sql.sql", "Quest {} has `ItemDrop{}` = 0 but `ItemDropQuantity{}` = {}.",
5048 qinfo->GetQuestId(), j+1, j+1, qinfo->ItemDropQuantity[j]);
5049 // no changes, quest ignore this data
5050 }
5051 }
5052 }
5053
5054 for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
5055 {
5056 int32 id = qinfo->RequiredNpcOrGo[j];
5057 if (id < 0 && !sObjectMgr->GetGameObjectTemplate(-id))
5058 {
5059 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but gameobject {} does not exist, quest can't be done.",
5060 qinfo->GetQuestId(), j+1, id, uint32(-id));
5061 qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
5062 }
5063
5064 if (id > 0 && !sObjectMgr->GetCreatureTemplate(id))
5065 {
5066 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but creature with entry {} does not exist, quest can't be done.",
5067 qinfo->GetQuestId(), j+1, id, uint32(id));
5068 qinfo->RequiredNpcOrGo[j] = 0; // quest can't be done for this requirement
5069 }
5070
5071 if (id)
5072 {
5073 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
5074
5076
5077 if (!qinfo->RequiredNpcOrGoCount[j])
5078 {
5079 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = {} but `RequiredNpcOrGoCount{}` = 0, quest can't be done.",
5080 qinfo->GetQuestId(), j+1, id, j+1);
5081 // no changes, quest can be incorrectly done, but we already report this
5082 }
5083 }
5084 else if (qinfo->RequiredNpcOrGoCount[j]>0)
5085 {
5086 TC_LOG_ERROR("sql.sql", "Quest {} has `RequiredNpcOrGo{}` = 0 but `RequiredNpcOrGoCount{}` = {}.",
5087 qinfo->GetQuestId(), j+1, j+1, qinfo->RequiredNpcOrGoCount[j]);
5088 // no changes, quest ignore this data
5089 }
5090 }
5091
5092 for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
5093 {
5094 uint32 id = qinfo->RewardChoiceItemId[j];
5095 if (id)
5096 {
5097 if (!sObjectMgr->GetItemTemplate(id))
5098 {
5099 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5100 qinfo->GetQuestId(), j+1, id, id);
5101 qinfo->RewardChoiceItemId[j] = 0; // no changes, quest will not reward this
5102 }
5103
5104 if (!qinfo->RewardChoiceItemCount[j])
5105 {
5106 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = {} but `RewardChoiceItemCount{}` = 0, quest can't be done.",
5107 qinfo->GetQuestId(), j+1, id, j+1);
5108 // no changes, quest can't be done
5109 }
5110 }
5111 else if (qinfo->RewardChoiceItemCount[j]>0)
5112 {
5113 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardChoiceItemId{}` = 0 but `RewardChoiceItemCount{}` = {}.",
5114 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardChoiceItemCount[j]);
5115 // no changes, quest ignore this data
5116 }
5117 }
5118
5119 for (uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j)
5120 {
5121 uint32 id = qinfo->RewardItemId[j];
5122 if (id)
5123 {
5124 if (!sObjectMgr->GetItemTemplate(id))
5125 {
5126 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but item with entry {} does not exist, quest will not reward this item.",
5127 qinfo->GetQuestId(), j+1, id, id);
5128 qinfo->RewardItemId[j] = 0; // no changes, quest will not reward this item
5129 }
5130
5131 if (!qinfo->RewardItemIdCount[j])
5132 {
5133 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = {} but `RewardItemIdCount{}` = 0, quest will not reward this item.",
5134 qinfo->GetQuestId(), j+1, id, j+1);
5135 // no changes
5136 }
5137 }
5138 else if (qinfo->RewardItemIdCount[j]>0)
5139 {
5140 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardItemId{}` = 0 but `RewardItemIdCount{}` = {}.",
5141 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardItemIdCount[j]);
5142 // no changes, quest ignore this data
5143 }
5144 }
5145
5146 for (uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
5147 {
5148 if (qinfo->RewardFactionId[j])
5149 {
5150 if (std::abs(qinfo->RewardFactionValueId[j]) > 9)
5151 {
5152 TC_LOG_ERROR("sql.sql", "Quest {} has RewardFactionValueId{} = {}. That is outside the range of valid values (-9 to 9).", qinfo->GetQuestId(), j+1, qinfo->RewardFactionValueId[j]);
5153 }
5154 if (!sFactionStore.LookupEntry(qinfo->RewardFactionId[j]))
5155 {
5156 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]);
5157 qinfo->RewardFactionId[j] = 0; // quest will not reward this
5158 }
5159 }
5160
5161 else if (qinfo->RewardFactionValueIdOverride[j] != 0)
5162 {
5163 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardFactionId{}` = 0 but `RewardFactionValueIdOverride{}` = {}.",
5164 qinfo->GetQuestId(), j+1, j+1, qinfo->RewardFactionValueIdOverride[j]);
5165 // no changes, quest ignore this data
5166 }
5167 }
5168
5169 if (qinfo->_rewardDisplaySpell)
5170 {
5171 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardDisplaySpell);
5172 if (!spellInfo)
5173 {
5174 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} does not exist, spell removed as display reward.",
5175 qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
5176 qinfo->_rewardDisplaySpell = 0; // no spell reward will display for this quest
5177 }
5178
5179 else if (!SpellMgr::IsSpellValid(spellInfo))
5180 {
5181 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} is broken, quest will not have a spell reward.",
5182 qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
5183 qinfo->_rewardDisplaySpell = 0; // no spell reward will display for this quest
5184 }
5185
5186 else if (GetTalentSpellCost(qinfo->_rewardDisplaySpell))
5187 {
5188 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} is talent, quest will not have a spell reward.",
5189 qinfo->GetQuestId(), qinfo->_rewardDisplaySpell, qinfo->_rewardDisplaySpell);
5190 qinfo->_rewardDisplaySpell = 0; // no spell reward will display for this quest
5191 }
5192 }
5193
5194 if (qinfo->_rewardSpell > 0)
5195 {
5196 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(qinfo->_rewardSpell);
5197 if (!spellInfo)
5198 {
5199 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpell` = {} but spell {} does not exist, quest will not have a spell reward.",
5200 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5201 qinfo->_rewardSpell = 0; // no spell will be cast on player
5202 }
5203
5204 else if (!SpellMgr::IsSpellValid(spellInfo))
5205 {
5206 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardSpell` = {} but spell {} is broken, quest will not have a spell reward.",
5207 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5208 qinfo->_rewardSpell = 0; // no spell will be cast on player
5209 }
5210
5211 else if (GetTalentSpellCost(qinfo->_rewardSpell))
5212 {
5213 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardDisplaySpell` = {} but spell {} is talent, quest will not have a spell reward.",
5214 qinfo->GetQuestId(), qinfo->_rewardSpell, qinfo->_rewardSpell);
5215 qinfo->_rewardSpell = 0; // no spell will be cast on player
5216 }
5217 }
5218
5219 if (qinfo->_rewardMailTemplateId)
5220 {
5221 if (!sMailTemplateStore.LookupEntry(qinfo->_rewardMailTemplateId))
5222 {
5223 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} does not exist, quest will not have a mail reward.",
5224 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId);
5225 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5226 qinfo->_rewardMailDelay = 0; // no mail will send to player
5227 qinfo->_rewardMailSenderEntry = 0;
5228 }
5229 else if (usedMailTemplates.find(qinfo->_rewardMailTemplateId) != usedMailTemplates.end())
5230 {
5231 auto used_mt_itr = usedMailTemplates.find(qinfo->_rewardMailTemplateId);
5232 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardMailTemplateId` = {} but mail template {} already used for quest {}, quest will not have a mail reward.",
5233 qinfo->GetQuestId(), qinfo->_rewardMailTemplateId, qinfo->_rewardMailTemplateId, used_mt_itr->second);
5234 qinfo->_rewardMailTemplateId = 0; // no mail will send to player
5235 qinfo->_rewardMailDelay = 0; // no mail will send to player
5236 qinfo->_rewardMailSenderEntry = 0;
5237 }
5238 else
5239 usedMailTemplates.emplace(qinfo->_rewardMailTemplateId, qinfo->GetQuestId());
5240 }
5241
5242 if (uint32 rewardNextQuest = qinfo->_rewardNextQuest)
5243 {
5244 if (!_questTemplates.count(rewardNextQuest))
5245 {
5246 TC_LOG_ERROR("sql.sql", "Quest {} has `RewardNextQuest` = {} but quest {} does not exist, quest chain will not work.",
5247 qinfo->GetQuestId(), qinfo->_rewardNextQuest, qinfo->_rewardNextQuest);
5248 qinfo->_rewardNextQuest = 0;
5249 }
5250 }
5251
5252 // fill additional data stores
5253 if (uint32 prevQuestId = std::abs(qinfo->_prevQuestId))
5254 {
5255 auto prevQuestItr = _questTemplates.find(prevQuestId);
5256 if (prevQuestItr == _questTemplates.end())
5257 TC_LOG_ERROR("sql.sql", "Quest {} has PrevQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_prevQuestId);
5258 else if (prevQuestItr->second->_breadcrumbForQuestId)
5259 TC_LOG_ERROR("sql.sql", "Quest {} should not be unlocked by breadcrumb quest {}", qinfo->_id, prevQuestId);
5260 else if (qinfo->_prevQuestId > 0)
5261 qinfo->DependentPreviousQuests.push_back(prevQuestId);
5262 }
5263
5264 if (uint32 nextQuestId = qinfo->_nextQuestId)
5265 {
5266 auto nextQuestItr = _questTemplates.find(nextQuestId);
5267 if (nextQuestItr == _questTemplates.end())
5268 TC_LOG_ERROR("sql.sql", "Quest {} has NextQuestId {}, but no such quest", qinfo->GetQuestId(), qinfo->_nextQuestId);
5269 else
5270 nextQuestItr->second->DependentPreviousQuests.push_back(qinfo->GetQuestId());
5271 }
5272
5273 if (uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId))
5274 {
5275 if (_questTemplates.find(breadcrumbForQuestId) == _questTemplates.end())
5276 {
5277 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb for quest {}, but no such quest exists", qinfo->_id, breadcrumbForQuestId);
5278 qinfo->_breadcrumbForQuestId = 0;
5279 }
5280 if (qinfo->_nextQuestId)
5281 TC_LOG_ERROR("sql.sql", "Quest {} is a breadcrumb, should not unlock quest {}", qinfo->_id, qinfo->_nextQuestId);
5282 }
5283
5284 if (qinfo->_exclusiveGroup)
5285 _exclusiveQuestGroups.emplace(qinfo->_exclusiveGroup, qinfo->GetQuestId());
5286 if (qinfo->_timeAllowed)
5288 if (qinfo->_requiredPlayerKills)
5290
5291 // Special flag to determine if quest is completed from the start, used to determine if we can fail timed quest if it is completed
5293 {
5294 bool addFlag = true;
5296 {
5297 for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
5298 {
5299 if (qinfo->RequiredItemId[j] != 0 && (qinfo->RequiredItemId[j] != qinfo->GetSrcItemId() || qinfo->RequiredItemCount[j] > qinfo->GetSrcItemCount()))
5300 {
5301 addFlag = false;
5302 break;
5303 }
5304 }
5305 }
5306
5307 if (addFlag)
5309 }
5310 }
5311
5312 // Disallow any breadcrumb loops and inform quests of their breadcrumbs
5313 for (auto& questPair : _questTemplates)
5314 {
5315 // skip post-loading checks for disabled quests
5316 if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, questPair.first, nullptr))
5317 continue;
5318
5319 Quest* qinfo = questPair.second.get();
5320 uint32 qid = qinfo->GetQuestId();
5321 uint32 breadcrumbForQuestId = std::abs(qinfo->_breadcrumbForQuestId);
5322 std::set<uint32> questSet;
5323
5324 while(breadcrumbForQuestId)
5325 {
5326 //a previously visited quest was found as a breadcrumb quest
5327 //breadcrumb loop found!
5328 if (!questSet.insert(qinfo->_id).second)
5329 {
5330 TC_LOG_ERROR("sql.sql", "Breadcrumb quests {} and {} are in a loop", qid, breadcrumbForQuestId);
5331 qinfo->_breadcrumbForQuestId = 0;
5332 break;
5333 }
5334
5335 qinfo = const_cast<Quest*>(sObjectMgr->GetQuestTemplate(breadcrumbForQuestId));
5336
5337 //every quest has a list of every breadcrumb towards it
5338 qinfo->DependentBreadcrumbQuests.push_back(qid);
5339
5340 breadcrumbForQuestId = qinfo->GetBreadcrumbForQuestId();
5341 }
5342 }
5343
5344 // check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
5345 for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
5346 {
5347 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
5348 if (!spellInfo)
5349 continue;
5350
5351 for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
5352 {
5353 if (spellEffectInfo.Effect != SPELL_EFFECT_QUEST_COMPLETE)
5354 continue;
5355
5356 uint32 quest_id = spellEffectInfo.MiscValue;
5357
5358 Quest const* quest = GetQuestTemplate(quest_id);
5359
5360 // some quest referenced in spells not exist (outdated spells)
5361 if (!quest)
5362 continue;
5363
5365 {
5366 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);
5367
5368 // this will prevent quest completing without objective
5369 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
5370 }
5371 }
5372 }
5373
5374 TC_LOG_INFO("server.loading", ">> Loaded {} quests definitions in {} ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
5375}
5376
5378{
5379 TC_LOG_INFO("server.loading", "Loading GO Start Quest Data...");
5381 TC_LOG_INFO("server.loading", "Loading GO End Quest Data...");
5383 TC_LOG_INFO("server.loading", "Loading Creature Start Quest Data...");
5385 TC_LOG_INFO("server.loading", "Loading Creature End Quest Data...");
5387}
5388
5390{
5391 uint32 oldMSTime = getMSTime();
5392
5393 _questLocaleStore.clear(); // need for reload case
5394
5395 // 0 1 2 3 4 5 6 7 8 9 10
5396 QueryResult result = WorldDatabase.Query("SELECT ID, locale, Title, Details, Objectives, EndText, CompletedText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4 FROM quest_template_locale");
5397 if (!result)
5398 return;
5399
5400 do
5401 {
5402 Field* fields = result->Fetch();
5403
5404 uint32 id = fields[0].GetUInt32();
5405 std::string localeName = fields[1].GetString();
5406
5407 LocaleConstant locale = GetLocaleByName(localeName);
5408 if (locale == LOCALE_enUS)
5409 continue;
5410
5411 QuestLocale& data = _questLocaleStore[id];
5412 AddLocaleString(fields[2].GetString(), locale, data.Title);
5413 AddLocaleString(fields[3].GetString(), locale, data.Details);
5414 AddLocaleString(fields[4].GetString(), locale, data.Objectives);
5415 AddLocaleString(fields[5].GetString(), locale, data.AreaDescription);
5416 AddLocaleString(fields[6].GetString(), locale, data.CompletedText);
5417
5418 for (uint8 i = 0; i < 4; ++i)
5419 AddLocaleString(fields[i + 7].GetString(), locale, data.ObjectiveText[i]);
5420 } while (result->NextRow());
5421
5422 TC_LOG_INFO("server.loading", ">> Loaded {} Quest locale strings in {} ms", uint32(_questLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
5423}
5424
5426{
5427 uint32 oldMSTime = getMSTime();
5428
5429 ScriptMapMap* scripts = GetScriptsMapByType(type);
5430 if (!scripts)
5431 return;
5432
5433 std::string tableName = GetScriptsTableNameByType(type);
5434 if (tableName.empty())
5435 return;
5436
5437 if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
5438 return;
5439
5440 TC_LOG_INFO("server.loading", "Loading {}...", tableName);
5441
5442 scripts->clear(); // need for reload support
5443
5444 // 0 1 2 3 4 5 6 7 8 9
5445 QueryResult result = WorldDatabase.PQuery("SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o FROM {}", tableName);
5446
5447 if (!result)
5448 {
5449 TC_LOG_INFO("server.loading", ">> Loaded 0 script definitions. DB table `{}` is empty!", tableName);
5450 return;
5451 }
5452
5453 uint32 count = 0;
5454
5455 do
5456 {
5457 Field* fields = result->Fetch();
5458 ScriptInfo tmp;
5459 tmp.type = type;
5460 tmp.id = fields[0].GetUInt32();
5461 tmp.delay = fields[1].GetUInt32();
5462 tmp.command = ScriptCommands(fields[2].GetUInt32());
5463 tmp.Raw.nData[0] = fields[3].GetUInt32();
5464 tmp.Raw.nData[1] = fields[4].GetUInt32();
5465 tmp.Raw.nData[2] = fields[5].GetInt32();
5466 tmp.Raw.fData[0] = fields[6].GetFloat();
5467 tmp.Raw.fData[1] = fields[7].GetFloat();
5468 tmp.Raw.fData[2] = fields[8].GetFloat();
5469 tmp.Raw.fData[3] = fields[9].GetFloat();
5470
5471 // generic command args check
5472 switch (tmp.command)
5473 {
5475 {
5477 {
5478 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk type (datalong = {}) in SCRIPT_COMMAND_TALK for script id {}",
5479 tableName, tmp.Talk.ChatType, tmp.id);
5480 continue;
5481 }
5482 if (!sObjectMgr->GetBroadcastText(uint32(tmp.Talk.TextID)))
5483 {
5484 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid talk text id (dataint = {}) in SCRIPT_COMMAND_TALK for script id {}",
5485 tableName, tmp.Talk.TextID, tmp.id);
5486 continue;
5487 }
5488
5489 break;
5490 }
5491
5493 {
5494 if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
5495 {
5496 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid emote id (datalong = {}) in SCRIPT_COMMAND_EMOTE for script id {}",
5497 tableName, tmp.Emote.EmoteID, tmp.id);
5498 continue;
5499 }
5500 break;
5501 }
5502
5504 {
5505 if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
5506 {
5507 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid map (Id: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5508 tableName, tmp.TeleportTo.MapID, tmp.id);
5509 continue;
5510 }
5511
5513 {
5514 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TELEPORT_TO for script id {}",
5515 tableName, tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
5516 continue;
5517 }
5518 break;
5519 }
5520
5522 {
5523 Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
5524 if (!quest)
5525 {
5526 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid quest (ID: {}) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id {}",
5527 tableName, tmp.QuestExplored.QuestID, tmp.id);
5528 continue;
5529 }
5530
5532 {
5533 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.",
5534 tableName, tmp.QuestExplored.QuestID, tmp.id);
5535
5536 // this will prevent quest completing without objective
5537 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
5538
5539 // continue; - quest objective requirement set and command can be allowed
5540 }
5541
5543 {
5544 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 {}",
5545 tableName, tmp.QuestExplored.Distance, tmp.id);
5546 continue;
5547 }
5548
5550 {
5551 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",
5553 continue;
5554 }
5555
5557 {
5558 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",
5559 tableName, tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
5560 continue;
5561 }
5562
5563 break;
5564 }
5565
5567 {
5569 {
5570 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_KILL_CREDIT for script id {}",
5571 tableName, tmp.KillCredit.CreatureEntry, tmp.id);
5572 continue;
5573 }
5574 break;
5575 }
5576
5578 {
5580 if (!data)
5581 {
5582 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5583 tableName, tmp.RespawnGameobject.GOGuid, tmp.id);
5584 continue;
5585 }
5586
5587 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5588 if (!info)
5589 {
5590 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5591 tableName, tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
5592 continue;
5593 }
5594
5595 if (info->type == GAMEOBJECT_TYPE_FISHINGNODE ||
5597 info->type == GAMEOBJECT_TYPE_DOOR ||
5598 info->type == GAMEOBJECT_TYPE_BUTTON ||
5599 info->type == GAMEOBJECT_TYPE_TRAP)
5600 {
5601 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id {}",
5602 tableName, info->entry, tmp.id);
5603 continue;
5604 }
5605 break;
5606 }
5607
5609 {
5611 {
5612 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid coordinates (X: {} Y: {} Z: {} O: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5614 continue;
5615 }
5616
5618 {
5619 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid creature (Entry: {}) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id {}",
5620 tableName, tmp.TempSummonCreature.CreatureEntry, tmp.id);
5621 continue;
5622 }
5623 break;
5624 }
5625
5628 {
5630 if (!data)
5631 {
5632 TC_LOG_ERROR("sql.sql", "Table `{}` has invalid gameobject (GUID: {}) in {} for script id {}",
5633 tableName, tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command), tmp.id);
5634 continue;
5635 }
5636
5637 GameObjectTemplate const* info = GetGameObjectTemplate(data->id);
5638 if (!info)
5639 {
5640 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject with invalid entry (GUID: {} Entry: {}) in {} for script id {}",
5641 tableName, tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command), tmp.id);
5642 continue;
5643 }
5644
5645 if (info->type != GAMEOBJECT_TYPE_DOOR)
5646 {
5647 TC_LOG_ERROR("sql.sql", "Table `{}` has gameobject type ({}) unsupported by command {} for script id {}",
5648 tableName, info->entry, GetScriptCommandName(tmp.command), tmp.id);
5649 continue;
5650 }
5651
5652 break;
5653 }
5654
5656 {
5657 if (!sSpellMgr->GetSpellInfo(tmp.RemoveAura.SpellID))
5658 {
5659 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5660 tableName, tmp.RemoveAura.SpellID, tmp.id);
5661 continue;
5662 }
5663 if (tmp.RemoveAura.Flags & ~0x1) // 1 bits (0, 1)
5664 {
5665 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in datalong2 ({}) in SCRIPT_COMMAND_REMOVE_AURA for script id {}",
5666 tableName, tmp.RemoveAura.Flags, tmp.id);
5667 continue;
5668 }
5669 break;
5670 }
5671
5673 {
5674 if (!sSpellMgr->GetSpellInfo(tmp.CastSpell.SpellID))
5675 {
5676 TC_LOG_ERROR("sql.sql", "Table `{}` using non-existent spell (id: {}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5677 tableName, tmp.CastSpell.SpellID, tmp.id);
5678 continue;
5679 }
5680 if (tmp.CastSpell.Flags > 4) // targeting type
5681 {
5682 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown target in datalong2 ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5683 tableName, tmp.CastSpell.Flags, tmp.id);
5684 continue;
5685 }
5686 if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1) // 1 bit (0, 1)
5687 {
5688 TC_LOG_ERROR("sql.sql", "Table `{}` using unknown flags in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5689 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5690 continue;
5691 }
5692 else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
5693 {
5694 TC_LOG_ERROR("sql.sql", "Table `{}` using invalid creature entry in dataint ({}) in SCRIPT_COMMAND_CAST_SPELL for script id {}",
5695 tableName, tmp.CastSpell.CreatureEntry, tmp.id);
5696 continue;
5697 }
5698 break;
5699 }
5700
5702 {
5704 {
5705 TC_LOG_ERROR("sql.sql", "Table `{}` has nonexistent item (entry: {}) in SCRIPT_COMMAND_CREATE_ITEM for script id {}",
5706 tableName, tmp.CreateItem.ItemEntry, tmp.id);
5707 continue;
5708 }
5709 if (!tmp.CreateItem.Amount)
5710 {
5711 TC_LOG_ERROR("sql.sql", "Table `{}` SCRIPT_COMMAND_CREATE_ITEM but amount is {} for script id {}",
5712 tableName, tmp.CreateItem.Amount, tmp.id);
5713 continue;
5714 }
5715 break;
5716 }
5717 default:
5718 break;
5719 }
5720
5721 if (scripts->find(tmp.id) == scripts->end())
5722 {
5723 ScriptMap emptyMap;
5724 (*scripts)[tmp.id] = emptyMap;
5725 }
5726 (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
5727
5728 ++count;
5729 }
5730 while (result->NextRow());
5731
5732 TC_LOG_INFO("server.loading", ">> Loaded {} script definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5733}
5734
5736{
5738
5739 std::set<uint32> evt_scripts;
5740 // Load all possible script entries from gameobjects
5741 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
5742 if (uint32 eventId = gameObjectTemplatePair.second.GetEventScriptId())
5743 evt_scripts.insert(eventId);
5744
5745 // Load all possible script entries from spells
5746 for (uint32 i = 1; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
5747 if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(i))
5748 for (SpellEffectInfo const& spellEffectInfo : spell->GetEffects())
5749 if (spellEffectInfo.IsEffect(SPELL_EFFECT_SEND_EVENT))
5750 if (spellEffectInfo.MiscValue)
5751 evt_scripts.insert(spellEffectInfo.MiscValue);
5752
5753 for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
5754 {
5755 for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
5756 {
5757 TaxiPathNodeEntry const* node = sTaxiPathNodesByPath[path_idx][node_idx];
5758
5759 if (node->ArrivalEventID)
5760 evt_scripts.insert(node->ArrivalEventID);
5761
5762 if (node->DepartureEventID)
5763 evt_scripts.insert(node->DepartureEventID);
5764 }
5765 }
5766
5767 // Then check if all scripts are in above list of possible script entries
5768 for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
5769 {
5770 std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
5771 if (itr2 == evt_scripts.end())
5772 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 {}",
5773 itr->first, SPELL_EFFECT_SEND_EVENT);
5774 }
5775}
5776
5777//Load WP Scripts
5779{
5781
5782 std::set<uint32> actionSet;
5783
5784 for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
5785 actionSet.insert(itr->first);
5786
5788 PreparedQueryResult result = WorldDatabase.Query(stmt);
5789
5790 if (result)
5791 {
5792 do
5793 {
5794 Field* fields = result->Fetch();
5795 uint32 action = fields[0].GetUInt32();
5796
5797 actionSet.erase(action);
5798 }
5799 while (result->NextRow());
5800 }
5801
5802 for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
5803 TC_LOG_ERROR("sql.sql", "There is no waypoint which links to the waypoint script {}", *itr);
5804}
5805
5807{
5808 uint32 oldMSTime = getMSTime();
5809
5810 _spellScriptsStore.clear(); // need for reload case
5811
5812 QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");
5813
5814 if (!result)
5815 {
5816 TC_LOG_INFO("server.loading", ">> Loaded 0 spell script names. DB table `spell_script_names` is empty!");
5817 return;
5818 }
5819
5820 uint32 count = 0;
5821
5822 do
5823 {
5824
5825 Field* fields = result->Fetch();
5826
5827 int32 spellId = fields[0].GetInt32();
5828 std::string const scriptName = fields[1].GetString();
5829
5830 bool allRanks = false;
5831 if (spellId < 0)
5832 {
5833 allRanks = true;
5834 spellId = -spellId;
5835 }
5836
5837 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
5838 if (!spellInfo)
5839 {
5840 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) does not exist.", scriptName, fields[0].GetInt32());
5841 continue;
5842 }
5843
5844 if (allRanks)
5845 {
5846 if (!spellInfo->IsRanked())
5847 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) has no ranks of spell.", scriptName, fields[0].GetInt32());
5848
5849 if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId))
5850 {
5851 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is not first rank of spell.", scriptName, fields[0].GetInt32());
5852 continue;
5853 }
5854
5855 while (spellInfo)
5856 {
5857 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
5858 spellInfo = spellInfo->GetNextRankSpell();
5859 }
5860 }
5861 else
5862 {
5863 if (spellInfo->IsRanked())
5864 TC_LOG_ERROR("sql.sql", "Scriptname: `{}` spell (Id: {}) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName, spellId);
5865
5866 _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
5867 }
5868
5869 ++count;
5870 }
5871 while (result->NextRow());
5872
5873 TC_LOG_INFO("server.loading", ">> Loaded {} spell script names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5874}
5875
5877{
5878 uint32 oldMSTime = getMSTime();
5879
5880 if (_spellScriptsStore.empty())
5881 {
5882 TC_LOG_INFO("server.loading", ">> Validated 0 scripts.");
5883 return;
5884 }
5885
5886 uint32 count = 0;
5887
5888 for (auto spell : _spellScriptsStore)
5889 {
5890 SpellInfo const* spellEntry = sSpellMgr->AssertSpellInfo(spell.first);
5891
5892 auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
5893
5894 for (auto itr = bounds.first; itr != bounds.second; ++itr)
5895 {
5896 if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
5897 {
5898 ++count;
5899
5900 std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
5901 std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
5902
5903 if (!spellScript && !auraScript)
5904 {
5905 TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `{}` do not return objects - script skipped", GetScriptName(itr->second.first));
5906
5907 itr->second.second = false;
5908 continue;
5909 }
5910
5911 if (spellScript)
5912 {
5913 spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
5914 spellScript->_Register();
5915
5916 if (!spellScript->_Validate(spellEntry))
5917 {
5918 itr->second.second = false;
5919 continue;
5920 }
5921 }
5922
5923 if (auraScript)
5924 {
5925 auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
5926 auraScript->_Register();
5927
5928 if (!auraScript->_Validate(spellEntry))
5929 {
5930 itr->second.second = false;
5931 continue;
5932 }
5933 }
5934
5935 // Enable the script when all checks passed
5936 itr->second.second = true;
5937 }
5938 else
5939 itr->second.second = false;
5940 }
5941 }
5942
5943 TC_LOG_INFO("server.loading", ">> Validated {} scripts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5944}
5945
5947{
5948 uint32 oldMSTime = getMSTime();
5949
5950 // 0 1 2
5951 QueryResult result = WorldDatabase.Query("SELECT ID, `Text`, NextPageID FROM page_text");
5952
5953 if (!result)
5954 {
5955 TC_LOG_INFO("server.loading", ">> Loaded 0 page texts. DB table `page_text` is empty!");
5956 return;
5957 }
5958
5959 uint32 count = 0;
5960 do
5961 {
5962 Field* fields = result->Fetch();
5963
5964 PageText& pageText = _pageTextStore[fields[0].GetUInt32()];
5965
5966 pageText.Text = fields[1].GetString();
5967 pageText.NextPageID = fields[2].GetUInt32();
5968
5969 ++count;
5970 }
5971 while (result->NextRow());
5972
5973 for (PageTextContainer::const_iterator itr = _pageTextStore.begin(); itr != _pageTextStore.end(); ++itr)
5974 {
5975 if (itr->second.NextPageID)
5976 {
5977 PageTextContainer::const_iterator itr2 = _pageTextStore.find(itr->second.NextPageID);
5978 if (itr2 == _pageTextStore.end())
5979 TC_LOG_ERROR("sql.sql", "Page text (ID: {}) has non-existing `NextPageID` ({})", itr->first, itr->second.NextPageID);
5980
5981 }
5982 }
5983
5984 TC_LOG_INFO("server.loading", ">> Loaded {} page texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
5985}
5986
5988{
5989 PageTextContainer::const_iterator itr = _pageTextStore.find(pageEntry);
5990 if (itr != _pageTextStore.end())
5991 return &(itr->second);
5992
5993 return nullptr;
5994}
5995
5997{
5998 uint32 oldMSTime = getMSTime();
5999
6000 _pageTextLocaleStore.clear(); // need for reload case
6001
6002 // 0 1 2
6003 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text` FROM page_text_locale");
6004
6005 if (!result)
6006 return;
6007
6008 do
6009 {
6010 Field* fields = result->Fetch();
6011
6012 uint32 id = fields[0].GetUInt32();
6013 std::string localeName = fields[1].GetString();
6014
6015 LocaleConstant locale = GetLocaleByName(localeName);
6016 if (locale == LOCALE_enUS)
6017 continue;
6018
6020 AddLocaleString(fields[2].GetString(), locale, data.Text);
6021 } while (result->NextRow());
6022
6023 TC_LOG_INFO("server.loading", ">> Loaded {} PageText locale strings in {} ms", uint32(_pageTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6024}
6025
6027{
6028 uint32 oldMSTime = getMSTime();
6029
6030 // 0 1 2 4
6031 QueryResult result = WorldDatabase.Query("SELECT map, parent, script, allowMount FROM instance_template");
6032
6033 if (!result)
6034 {
6035 TC_LOG_INFO("server.loading", ">> Loaded 0 instance templates. DB table `page_text` is empty!");
6036 return;
6037 }
6038
6039 uint32 count = 0;
6040 do
6041 {
6042 Field* fields = result->Fetch();
6043
6044 uint16 mapID = fields[0].GetUInt16();
6045
6046 if (!MapManager::IsValidMAP(mapID, true))
6047 {
6048 TC_LOG_ERROR("sql.sql", "ObjectMgr::LoadInstanceTemplate: bad mapid {} for template!", mapID);
6049 continue;
6050 }
6051
6052 InstanceTemplate instanceTemplate;
6053
6054 instanceTemplate.AllowMount = fields[3].GetBool();
6055 instanceTemplate.Parent = uint32(fields[1].GetUInt16());
6056 instanceTemplate.ScriptId = sObjectMgr->GetScriptId(fields[2].GetString());
6057
6058 _instanceTemplateStore[mapID] = instanceTemplate;
6059
6060 ++count;
6061 }
6062 while (result->NextRow());
6063
6064 TC_LOG_INFO("server.loading", ">> Loaded {} instance templates in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6065}
6066
6068{
6069 InstanceTemplateContainer::const_iterator itr = _instanceTemplateStore.find(uint16(mapID));
6070 if (itr != _instanceTemplateStore.end())
6071 return &(itr->second);
6072
6073 return nullptr;
6074}
6075
6077{
6078 uint32 oldMSTime = getMSTime();
6079
6080 // 0 1 2 3
6081 QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
6082 if (!result)
6083 {
6084 TC_LOG_INFO("server.loading", ">> Loaded 0 instance encounters, table is empty!");
6085 return;
6086 }
6087
6088 uint32 count = 0;
6089 std::map<uint32, DungeonEncounterEntry const*> dungeonLastBosses;
6090 do
6091 {
6092 Field* fields = result->Fetch();
6093 uint32 entry = fields[0].GetUInt32();
6094 uint8 creditType = fields[1].GetUInt8();
6095 uint32 creditEntry = fields[2].GetUInt32();
6096 uint32 lastEncounterDungeon = fields[3].GetUInt16();
6097 DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
6098 if (!dungeonEncounter)
6099 {
6100 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid encounter id {}, skipped!", entry);
6101 continue;
6102 }
6103
6104 if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeonEntry(lastEncounterDungeon))
6105 {
6106 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an encounter {} ({}) marked as final for invalid dungeon id {}, skipped!", entry, dungeonEncounter->Name[0], lastEncounterDungeon);
6107 continue;
6108 }
6109
6110 auto itr = dungeonLastBosses.find(lastEncounterDungeon);
6111 if (lastEncounterDungeon)
6112 {
6113 if (itr != dungeonLastBosses.end())
6114 {
6115 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]);
6116 continue;
6117 }
6118
6119 dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
6120 }
6121
6122 switch (creditType)
6123 {
6125 {
6126 CreatureTemplate const* creatureInfo = GetCreatureTemplate(creditEntry);
6127 if (!creatureInfo)
6128 {
6129 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid creature (entry {}) linked to the encounter {} ({}), skipped!", creditEntry, entry, dungeonEncounter->Name[0]);
6130 continue;
6131 }
6132 const_cast<CreatureTemplate*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
6133 for (uint8 diff = 1; diff < MAX_DIFFICULTY; ++diff)
6134 {
6135 if (uint32 diffEntry = creatureInfo->DifficultyEntry[diff - 1])
6136 {
6137 if (CreatureTemplate const* diffInfo = GetCreatureTemplate(diffEntry))
6139 }
6140 }
6141 break;
6142 }
6144 if (!sSpellMgr->GetSpellInfo(creditEntry))
6145 {
6146 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid spell (entry {}) linked to the encounter {} ({}), skipped!", creditEntry, entry, dungeonEncounter->Name[0]);
6147 continue;
6148 }
6149 break;
6150 default:
6151 TC_LOG_ERROR("sql.sql", "Table `instance_encounters` has an invalid credit type ({}) for encounter {} ({}), skipped!", creditType, entry, dungeonEncounter->Name[0]);
6152 continue;
6153 }
6154
6155 DungeonEncounterList& encounters = _dungeonEncounterStore[MAKE_PAIR32(dungeonEncounter->MapID, dungeonEncounter->Difficulty)];
6156 encounters.emplace_back(std::make_unique<DungeonEncounter>(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
6157 ++count;
6158 } while (result->NextRow());
6159
6160 TC_LOG_INFO("server.loading", ">> Loaded {} instance encounters in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6161}
6162
6164{
6165 GossipTextContainer::const_iterator itr = _gossipTextStore.find(Text_ID);
6166 if (itr != _gossipTextStore.end())
6167 return &itr->second;
6168 return nullptr;
6169}
6170
6172{
6173 uint32 oldMSTime = getMSTime();
6174
6175 QueryResult result = WorldDatabase.Query("SELECT ID, "
6176 "text0_0, text0_1, BroadcastTextID0, lang0, Probability0, EmoteDelay0_0, Emote0_0, EmoteDelay0_1, Emote0_1, EmoteDelay0_2, Emote0_2, "
6177 "text1_0, text1_1, BroadcastTextID1, lang1, Probability1, EmoteDelay1_0, Emote1_0, EmoteDelay1_1, Emote1_1, EmoteDelay1_2, Emote1_2, "
6178 "text2_0, text2_1, BroadcastTextID2, lang2, Probability2, EmoteDelay2_0, Emote2_0, EmoteDelay2_1, Emote2_1, EmoteDelay2_2, Emote2_2, "
6179 "text3_0, text3_1, BroadcastTextID3, lang3, Probability3, EmoteDelay3_0, Emote3_0, EmoteDelay3_1, Emote3_1, EmoteDelay3_2, Emote3_2, "
6180 "text4_0, text4_1, BroadcastTextID4, lang4, Probability4, EmoteDelay4_0, Emote4_0, EmoteDelay4_1, Emote4_1, EmoteDelay4_2, Emote4_2, "
6181 "text5_0, text5_1, BroadcastTextID5, lang5, Probability5, EmoteDelay5_0, Emote5_0, EmoteDelay5_1, Emote5_1, EmoteDelay5_2, Emote5_2, "
6182 "text6_0, text6_1, BroadcastTextID6, lang6, Probability6, EmoteDelay6_0, Emote6_0, EmoteDelay6_1, Emote6_1, EmoteDelay6_2, Emote6_2, "
6183 "text7_0, text7_1, BroadcastTextID7, lang7, Probability7, EmoteDelay7_0, Emote7_0, EmoteDelay7_1, Emote7_1, EmoteDelay7_2, Emote7_2 "
6184 "FROM npc_text");
6185
6186 if (!result)
6187 {
6188 TC_LOG_INFO("server.loading", ">> Loaded 0 npc texts, table is empty!");
6189 return;
6190 }
6191
6192 _gossipTextStore.rehash(result->GetRowCount());
6193
6194 uint32 count = 0;
6195 uint8 cic;
6196
6197 do
6198 {
6199 ++count;
6200 cic = 0;
6201
6202 Field* fields = result->Fetch();
6203
6204 uint32 id = fields[cic++].GetUInt32();
6205 if (!id)
6206 {
6207 TC_LOG_ERROR("sql.sql", "Table `npc_text` has record with reserved id 0, ignore.");
6208 continue;
6209 }
6210
6211 GossipText& gText = _gossipTextStore[id];
6212
6213 for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
6214 {
6215 GossipTextOption& gOption = gText.Options[i];
6216 gOption.Text_0 = fields[cic++].GetString();
6217 gOption.Text_1 = fields[cic++].GetString();
6218 gOption.BroadcastTextID = fields[cic++].GetUInt32();
6219 gOption.Language = fields[cic++].GetUInt8();
6220 gOption.Probability = fields[cic++].GetFloat();
6221
6222 for (uint8 j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
6223 {
6224 gOption.Emotes[j]._Delay = fields[cic++].GetUInt16();
6225 gOption.Emotes[j]._Emote = fields[cic++].GetUInt16();
6226 }
6227
6228 // check broadcast_text correctness
6229 if (gOption.BroadcastTextID)
6230 {
6231 if (BroadcastText const* bcText = sObjectMgr->GetBroadcastText(gOption.BroadcastTextID))
6232 {
6233 if (bcText->Text[DEFAULT_LOCALE] != gOption.Text_0)
6234 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);
6235 if (bcText->Text1[DEFAULT_LOCALE] != gOption.Text_1)
6236 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);
6237 }
6238 else
6239 {
6240 TC_LOG_ERROR("sql.sql", "GossipText (Id: {}) in table `npc_text` has non-existing or incompatible BroadcastTextID{} {}.", id, i, gOption.BroadcastTextID);
6241 gOption.BroadcastTextID = 0;
6242 }
6243
6244 }
6245 }
6246
6247 }
6248 while (result->NextRow());
6249
6250 TC_LOG_INFO("server.loading", ">> Loaded {} npc texts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6251}
6252
6254{
6255 uint32 oldMSTime = getMSTime();
6256
6257 _npcTextLocaleStore.clear(); // need for reload case
6258
6259 QueryResult result = WorldDatabase.Query("SELECT ID, Locale, "
6260 // 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
6261 "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 "
6262 "FROM npc_text_locale");
6263
6264 if (!result)
6265 return;
6266
6267 do
6268 {
6269 Field* fields = result->Fetch();
6270
6271 uint32 id = fields[0].GetUInt32();
6272 std::string localeName = fields[1].GetString();
6273
6274 LocaleConstant locale = GetLocaleByName(localeName);
6275 if (locale == LOCALE_enUS)
6276 continue;
6277
6279 for (uint8 i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; ++i)
6280 {
6281 AddLocaleString(fields[2 + i * 2].GetString(), locale, data.Text_0[i]);
6282 AddLocaleString(fields[3 + i * 2].GetString(), locale, data.Text_1[i]);
6283 }
6284 } while (result->NextRow());
6285
6286 TC_LOG_INFO("server.loading", ">> Loaded {} NpcText locale strings in {} ms", uint32(_npcTextLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6287}
6288
6289//not very fast function but it is called only once a day, or on starting-up
6291{
6292 uint32 oldMSTime = getMSTime();
6293
6294 time_t curTime = GameTime::GetGameTime();
6295 tm lt;
6296 localtime_r(&curTime, &lt);
6297 uint64 basetime(curTime);
6298 TC_LOG_INFO("misc", "Returning mails current time: hour: {}, minute: {}, second: {} ", lt.tm_hour, lt.tm_min, lt.tm_sec);
6299
6300 // Delete all old mails without item and without body immediately, if starting server
6301 if (!serverUp)
6302 {
6304 stmt->setUInt64(0, basetime);
6305 CharacterDatabase.Execute(stmt);
6306 }
6308 stmt->setUInt64(0, basetime);
6309 PreparedQueryResult result = CharacterDatabase.Query(stmt);
6310 if (!result)
6311 {
6312 TC_LOG_INFO("server.loading", ">> No expired mails found.");
6313 return; // any mails need to be returned or deleted
6314 }
6315
6316 std::map<uint32 /*messageId*/, MailItemInfoVec> itemsCache;
6317 stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS);
6318 stmt->setUInt32(0, (uint32)basetime);
6319 if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
6320 {
6321 MailItemInfo item;
6322 do
6323 {
6324 Field* fields = items->Fetch();
6325 item.item_guid = fields[0].GetUInt32();
6326 item.item_template = fields[1].GetUInt32();
6327 uint32 mailId = fields[2].GetUInt32();
6328 itemsCache[mailId].push_back(item);
6329 } while (items->NextRow());
6330 }
6331
6332 uint32 deletedCount = 0;
6333 uint32 returnedCount = 0;
6334 do
6335 {
6336 Field* fields = result->Fetch();
6337 ObjectGuid::LowType receiver = fields[3].GetUInt32();
6338 if (serverUp && ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create<HighGuid::Player>(receiver)))
6339 continue;
6340
6341 Mail* m = new Mail;
6342 m->messageID = fields[0].GetUInt32();
6343 m->messageType = fields[1].GetUInt8();
6344 m->sender = fields[2].GetUInt32();
6345 m->receiver = receiver;
6346 bool has_items = fields[4].GetBool();
6347 m->expire_time = time_t(fields[5].GetUInt32());
6348 m->deliver_time = 0;
6349 m->COD = fields[6].GetUInt32();
6350 m->checked = fields[7].GetUInt8();
6351 m->mailTemplateId = fields[8].GetInt16();
6352
6353 // Delete or return mail
6354 if (has_items)
6355 {
6356 // read items from cache
6357 m->items.swap(itemsCache[m->messageID]);
6358
6359 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
6361 {
6362 // mail open and then not returned
6363 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6364 {
6365 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
6366 stmt->setUInt32(0, itr2->item_guid);
6367 CharacterDatabase.Execute(stmt);
6368 }
6369
6370 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
6371 stmt->setUInt32(0, m->messageID);
6372 CharacterDatabase.Execute(stmt);
6373 }
6374 else
6375 {
6376 // Mail will be returned
6377 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
6378 stmt->setUInt32(0, m->receiver);
6379 stmt->setUInt32(1, m->sender);
6380 stmt->setUInt32(2, basetime + 30 * DAY);
6381 stmt->setUInt32(3, basetime);
6383 stmt->setUInt32(5, m->messageID);
6384 CharacterDatabase.Execute(stmt);
6385 for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
6386 {
6387 // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
6388 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
6389 stmt->setUInt32(0, m->sender);
6390 stmt->setUInt32(1, itr2->item_guid);
6391 CharacterDatabase.Execute(stmt);
6392
6393 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
6394 stmt->setUInt32(0, m->sender);
6395 stmt->setUInt32(1, itr2->item_guid);
6396 CharacterDatabase.Execute(stmt);
6397 }
6398 delete m;
6399 ++returnedCount;
6400 continue;
6401 }
6402 }
6403
6404 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
6405 stmt->setUInt32(0, m->messageID);
6406 CharacterDatabase.Execute(stmt);
6407 delete m;
6408 ++deletedCount;
6409 }
6410 while (result->NextRow());
6411
6412 TC_LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
6413}
6414
6416{
6417 uint32 oldMSTime = getMSTime();
6418
6419 _questAreaTriggerStore.clear(); // need for reload case
6420
6421 QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");
6422
6423 if (!result)
6424 {
6425 TC_LOG_INFO("server.loading", ">> Loaded 0 quest trigger points. DB table `areatrigger_involvedrelation` is empty.");
6426 return;
6427 }
6428
6429 uint32 count = 0;
6430
6431 do
6432 {
6433 ++count;
6434
6435 Field* fields = result->Fetch();
6436
6437 uint32 trigger_ID = fields[0].GetUInt32();
6438 uint32 quest_ID = fields[1].GetUInt32();
6439
6440 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
6441 if (!atEntry)
6442 {
6443 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", trigger_ID);
6444 continue;
6445 }
6446
6447 Quest const* quest = GetQuestTemplate(quest_ID);
6448
6449 if (!quest)
6450 {
6451 TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: {}) for not existing quest {}", trigger_ID, quest_ID);
6452 continue;
6453 }
6454
6456 {
6457 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);
6458
6459 // this will prevent quest completing without objective
6460 const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
6461
6462 // continue; - quest modified to required objective and trigger can be allowed.
6463 }
6464
6465 _questAreaTriggerStore[trigger_ID] = quest_ID;
6466
6467 } while (result->NextRow());
6468
6469 TC_LOG_INFO("server.loading", ">> Loaded {} quest trigger points in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6470}
6471
6473{
6474 auto itr = _questGreetingStore.find(guid.GetTypeId());
6475 if (itr == _questGreetingStore.end())
6476 return nullptr;
6477
6478 auto questItr = itr->second.find(guid.GetEntry());
6479 if (questItr == itr->second.end())
6480 return nullptr;
6481
6482 return &questItr->second;
6483}
6484
6486{
6487 uint32 oldMSTime = getMSTime();
6488
6489 _questGreetingStore.clear(); // need for reload case
6490
6491 // 0 1 2 3 4
6492 QueryResult result = WorldDatabase.Query("SELECT ID, Type, GreetEmoteType, GreetEmoteDelay, Greeting FROM quest_greeting");
6493 if (!result)
6494 {
6495 TC_LOG_INFO("server.loading", ">> Loaded 0 quest greetings. DB table `quest_greeting` is empty.");
6496 return;
6497 }
6498
6499 _questGreetingStore.rehash(result->GetRowCount());
6500
6501 uint32 count = 0;
6502
6503 do
6504 {
6505 Field* fields = result->Fetch();
6506
6507 uint32 id = fields[0].GetUInt32();
6508 uint8 type = fields[1].GetUInt8();
6509 // overwrite
6510 switch (type)
6511 {
6512 case 0: // Creature
6513 type = TYPEID_UNIT;
6514 if (!sObjectMgr->GetCreatureTemplate(id))
6515 {
6516 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: creature template (entry: {}) does not exist.", id);
6517 continue;
6518 }
6519 break;
6520 case 1: // GameObject
6521 type = TYPEID_GAMEOBJECT;
6522 if (!sObjectMgr->GetGameObjectTemplate(id))
6523 {
6524 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: gameobject template (entry: {}) does not exist.", id);
6525 continue;
6526 }
6527 break;
6528 default:
6529 TC_LOG_ERROR("sql.sql", "Table `quest_greeting`: unknown type = {} for entry = {}. Skipped.", type, id);
6530 continue;
6531 }
6532
6533 uint16 greetEmoteType = fields[2].GetUInt16();
6534
6535 if (greetEmoteType > 0 && !sEmotesStore.LookupEntry(greetEmoteType))
6536 {
6537 TC_LOG_DEBUG("sql.sql", "Table `quest_greeting`: entry {} has greetEmoteType = {} but emote does not exist. Set to 0.", id, greetEmoteType);
6538 greetEmoteType = 0;
6539 }
6540
6541 uint32 greetEmoteDelay = fields[3].GetUInt32();
6542 std::string greeting = fields[4].GetString();
6543
6544 _questGreetingStore[type][id] = QuestGreeting(greetEmoteType, greetEmoteDelay, greeting);
6545
6546 ++count;
6547 }
6548 while (result->NextRow());
6549
6550 TC_LOG_INFO("server.loading", ">> Loaded {} quest_greeting in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6551}
6552
6554{
6555 uint32 oldMSTime = getMSTime();
6556
6557 _questGreetingLocaleStore.clear(); // need for reload case
6558
6559 // 0 1 2 3
6560 QueryResult result = WorldDatabase.Query("SELECT ID, Type, Locale, Greeting FROM quest_greeting_locale");
6561 if (!result)
6562 {
6563 TC_LOG_INFO("server.loading", ">> Loaded 0 quest_greeting locales. DB table `quest_greeting_locale` is empty.");
6564 return;
6565 }
6566
6567 do
6568 {
6569 Field* fields = result->Fetch();
6570
6571 uint32 id = fields[0].GetUInt32();
6572 uint8 type = fields[1].GetUInt8();
6573 // overwrite
6574 switch (type)
6575 {
6576 case 0: // Creature
6577 type = TYPEID_UNIT;
6578 break;
6579 case 1: // GameObject
6580 type = TYPEID_GAMEOBJECT;
6581 break;
6582 default:
6583 break;
6584 }
6585
6586 std::string localeName = fields[2].GetString();
6587
6588 LocaleConstant locale = GetLocaleByName(localeName);
6589 if (locale == LOCALE_enUS)
6590 continue;
6591
6593 AddLocaleString(fields[3].GetString(), locale, data.greeting);
6594 } while (result->NextRow());
6595
6596 TC_LOG_INFO("server.loading", ">> Loaded {} quest greeting locale strings in {} ms", uint32(_questGreetingLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
6597}
6598
6600{
6601 uint32 oldMSTime = getMSTime();
6602
6603 _questOfferRewardLocaleStore.clear(); // need for reload case
6604 // 0 1 2
6605 QueryResult result = WorldDatabase.Query("SELECT Id, locale, RewardText FROM quest_offer_reward_locale");
6606 if (!result)
6607 return;
6608
6609 do
6610 {
6611 Field* fields = result->Fetch();
6612
6613 uint32 id = fields[0].GetUInt32();
6614 std::string localeName = fields[1].GetString();
6615
6616 LocaleConstant locale = GetLocaleByName(localeName);
6617 if (locale == LOCALE_enUS)
6618 continue;
6619
6621 AddLocaleString(fields[2].GetString(), locale, data.RewardText);
6622 } while (result->NextRow());
6623
6624 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Offer Reward locale strings in {} ms", _questOfferRewardLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
6625}
6626
6628{
6629 uint32 oldMSTime = getMSTime();
6630
6631 _questRequestItemsLocaleStore.clear(); // need for reload case
6632 // 0 1 2
6633 QueryResult result = WorldDatabase.Query("SELECT Id, locale, CompletionText FROM quest_request_items_locale");
6634 if (!result)
6635 return;
6636
6637 do
6638 {
6639 Field* fields = result->Fetch();
6640
6641 uint32 id = fields[0].GetUInt32();
6642 std::string localeName = fields[1].GetString();
6643
6644 LocaleConstant locale = GetLocaleByName(localeName);
6645 if (locale == LOCALE_enUS)
6646 continue;
6647
6649 AddLocaleString(fields[2].GetString(), locale, data.CompletionText);
6650 } while (result->NextRow());
6651
6652 TC_LOG_INFO("server.loading", ">> Loaded {} Quest Request Items locale strings in {} ms", _questRequestItemsLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
6653}
6654
6656{
6657 uint32 oldMSTime = getMSTime();
6658
6659 _tavernAreaTriggerStore.clear(); // need for reload case
6660
6661 QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
6662
6663 if (!result)
6664 {
6665 TC_LOG_INFO("server.loading", ">> Loaded 0 tavern triggers. DB table `areatrigger_tavern` is empty.");
6666 return;
6667 }
6668
6669 uint32 count = 0;
6670
6671 do
6672 {
6673 ++count;
6674
6675 Field* fields = result->Fetch();
6676
6677 uint32 Trigger_ID = fields[0].GetUInt32();
6678
6679 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
6680 if (!atEntry)
6681 {
6682 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
6683 continue;
6684 }
6685
6686 _tavernAreaTriggerStore.insert(Trigger_ID);
6687 } while (result->NextRow());
6688
6689 TC_LOG_INFO("server.loading", ">> Loaded {} tavern triggers in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6690}
6691
6693{
6694 uint32 oldMSTime = getMSTime();
6695
6696 _areaTriggerScriptStore.clear(); // need for reload case
6697
6698 QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
6699 if (!result)
6700 {
6701 TC_LOG_INFO("server.loading", ">> Loaded 0 areatrigger scripts. DB table `areatrigger_scripts` is empty.");
6702 return;
6703 }
6704
6705 do
6706 {
6707 Field* fields = result->Fetch();
6708
6709 uint32 triggerId = fields[0].GetUInt32();
6710 std::string const scriptName = fields[1].GetString();
6711
6712 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(triggerId);
6713 if (!atEntry)
6714 {
6715 TC_LOG_ERROR("sql.sql", "AreaTrigger (ID: {}) does not exist in `AreaTrigger.dbc`.", triggerId);
6716 continue;
6717 }
6718 _areaTriggerScriptStore[triggerId] = GetScriptId(scriptName);
6719 }
6720 while (result->NextRow());
6721
6722 TC_LOG_INFO("server.loading", ">> Loaded {} areatrigger scripts in {} ms", _areaTriggerScriptStore.size(), GetMSTimeDiffToNow(oldMSTime));
6723}
6724
6725uint32 ObjectMgr::GetNearestTaxiNode(float x, float y, float z, uint32 mapid, uint32 team)
6726{
6727 bool found = false;
6728 float dist = 10000;
6729 uint32 id = 0;
6730
6731 for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
6732 {
6733 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
6734
6735 if (!node || node->ContinentID != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981)) // dk flight
6736 continue;
6737
6738 uint32 field = uint32((node->ID - 1) / (sizeof(TaxiMask::value_type) * 8));
6739 TaxiMask::value_type submask = TaxiMask::value_type(1 << ((node->ID - 1) % (sizeof(TaxiMask::value_type) * 8)));
6740
6741 // skip not taxi network nodes
6742 if ((sTaxiNodesMask[field] & submask) == 0)
6743 continue;
6744
6745 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);
6746 if (found)
6747 {
6748 if (dist2 < dist)
6749 {
6750 dist = dist2;
6751 id = i;
6752 }
6753 }
6754 else
6755 {
6756 found = true;
6757 dist = dist2;
6758 id = i;
6759 }
6760 }
6761
6762 return id;
6763}
6764
6765void ObjectMgr::GetTaxiPath(uint32 source, uint32 destination, uint32 &path, uint32 &cost)
6766{
6767 TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
6768 if (src_i == sTaxiPathSetBySource.end())
6769 {
6770 path = 0;
6771 cost = 0;
6772 return;
6773 }
6774
6775 TaxiPathSetForSource& pathSet = src_i->second;
6776
6777 TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
6778 if (dest_i == pathSet.end())
6779 {
6780 path = 0;
6781 cost = 0;
6782 return;
6783 }
6784
6785 cost = dest_i->second.price;
6786 path = dest_i->second.ID;
6787}
6788
6789uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt_team /* = false */)
6790{
6791 uint32 mount_id = 0;
6792
6793 // select mount creature id
6794 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
6795 if (node)
6796 {
6797 uint32 mount_entry = 0;
6798 if (team == ALLIANCE)
6799 mount_entry = node->MountCreatureID[1];
6800 else
6801 mount_entry = node->MountCreatureID[0];
6802
6803 // Fix for Alliance not being able to use Acherus taxi
6804 // only one mount type for both sides
6805 if (mount_entry == 0 && allowed_alt_team)
6806 {
6807 // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
6808 mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
6809 }
6810
6811 CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry);
6812 if (mount_info)
6813 {
6814 mount_id = mount_info->GetRandomValidModelId();
6815 if (!mount_id)
6816 {
6817 TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry {}! Can't load it!", mount_entry);
6818 return 0;
6819 }
6820 }
6821 }
6822
6823 // minfo is not actually used but the mount_id was updated
6825
6826 return mount_id;
6827}
6828
6830{
6831 auto itr = _questTemplates.find(quest_id);
6832 return itr != _questTemplates.end() ? itr->second.get() : nullptr;
6833}
6834
6836{
6837 uint32 oldMSTime = getMSTime();
6838
6839 GraveyardStore.clear(); // need for reload case
6840
6841 // 0 1 2
6842 QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone");
6843
6844 if (!result)
6845 {
6846 TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty.");
6847 return;
6848 }
6849
6850 uint32 count = 0;
6851
6852 do
6853 {
6854 ++count;
6855
6856 Field* fields = result->Fetch();
6857
6858 uint32 safeLocId = fields[0].GetUInt32();
6859 uint32 zoneId = fields[1].GetUInt32();
6860 uint32 team = fields[2].GetUInt16();
6861
6862 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
6863 if (!entry)
6864 {
6865 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: {}), skipped.", safeLocId);
6866 continue;
6867 }
6868
6869 AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId);
6870 if (!areaEntry)
6871 {
6872 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: {}), skipped.", zoneId);
6873 continue;
6874 }
6875
6876 if (areaEntry->ParentAreaID != 0)
6877 {
6878 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for SubZone (ID: {}) instead of zone, skipped.", zoneId);
6879 continue;
6880 }
6881
6882 if (team != 0 && team != HORDE && team != ALLIANCE)
6883 {
6884 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction ({}), skipped.", team);
6885 continue;
6886 }
6887
6888 if (!AddGraveyardLink(safeLocId, zoneId, team, false))
6889 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: {}) and Zone (ID: {}), skipped.", safeLocId, zoneId);
6890 } while (result->NextRow());
6891
6892 TC_LOG_INFO("server.loading", ">> Loaded {} graveyard-zone links in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
6893}
6894
6896{
6897 enum DefaultGraveyard
6898 {
6899 HORDE_GRAVEYARD = 10, // Crossroads
6900 ALLIANCE_GRAVEYARD = 4 // Westfall
6901 };
6902
6903 if (team == HORDE)
6904 return sWorldSafeLocsStore.LookupEntry(HORDE_GRAVEYARD);
6905 else if (team == ALLIANCE)
6906 return sWorldSafeLocsStore.LookupEntry(ALLIANCE_GRAVEYARD);
6907 else return nullptr;
6908}
6909
6910WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(float x, float y, float z, uint32 MapId, uint32 team, WorldObject* conditionObject) const
6911{
6912 // search for zone associated closest graveyard
6913 uint32 zoneId = sMapMgr->GetZoneId(PHASEMASK_NORMAL, MapId, x, y, z);
6914
6915 if (!zoneId)
6916 {
6917 if (z > -500)
6918 {
6919 TC_LOG_ERROR("misc", "ZoneId not found for map {} coords ({}, {}, {}), object name: {} {}", MapId, x, y, z,
6920 conditionObject ? std::string_view(conditionObject->GetName()) : "", Object::GetGUID(conditionObject));
6921 return GetDefaultGraveyard(team);
6922 }
6923 }
6924
6925 // Simulate std. algorithm:
6926 // found some graveyard associated to (ghost_zone, ghost_map)
6927 //
6928 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
6929 // then check faction
6930 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
6931 // then check faction
6932 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
6933 MapEntry const* map = sMapStore.LookupEntry(MapId);
6934
6935 // not need to check validity of map object; MapId _MUST_ be valid here
6936 if (range.first == range.second && !map->IsBattlegroundOrArena())
6937 {
6938 if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones
6939 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
6940 return GetDefaultGraveyard(team);
6941 }
6942
6943 // at corpse map
6944 bool foundNear = false;
6945 float distNear = 10000;
6946 WorldSafeLocsEntry const* entryNear = nullptr;
6947
6948 // at entrance map for corpse map
6949 bool foundEntr = false;
6950 float distEntr = 10000;
6951 WorldSafeLocsEntry const* entryEntr = nullptr;
6952
6953 // some where other
6954 WorldSafeLocsEntry const* entryFar = nullptr;
6955
6956 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
6957
6958 for (; range.first != range.second; ++range.first)
6959 {
6960 GraveyardData const& data = range.first->second;
6961
6962 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
6963 if (!entry)
6964 {
6965 TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID {}), skipped.", data.safeLocId);
6966 continue;
6967 }
6968
6969 // skip enemy faction graveyard
6970 // team == 0 case can be at call from .neargrave
6971 if (data.team != 0 && team != 0 && data.team != team)
6972 continue;
6973
6974 // find now nearest graveyard at other map
6975 if (MapId != entry->Continent)
6976 {
6977 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
6978 if (!mapEntry
6979 || mapEntry->CorpseMapID < 0
6980 || uint32(mapEntry->CorpseMapID) != entry->Continent
6981 || (mapEntry->Corpse.X == 0 && mapEntry->Corpse.Y == 0))
6982 {
6983 // not have any corrdinates for check distance anyway
6984 entryFar = entry;
6985 continue;
6986 }
6987
6988 // at entrance map calculate distance (2D);
6989 float dist2 = (entry->Loc.X - mapEntry->Corpse.X)*(entry->Loc.X - mapEntry->Corpse.X)
6990 +(entry->Loc.Y - mapEntry->Corpse.Y)*(entry->Loc.Y - mapEntry->Corpse.Y);
6991 if (foundEntr)
6992 {
6993 if (dist2 < distEntr)
6994 {
6995 distEntr = dist2;
6996 entryEntr = entry;
6997 }
6998 }
6999 else
7000 {
7001 foundEntr = true;
7002 distEntr = dist2;
7003 entryEntr = entry;
7004 }
7005 }
7006 // find now nearest graveyard at same map
7007 else
7008 {
7009 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);
7010 if (foundNear)
7011 {
7012 if (dist2 < distNear)
7013 {
7014 distNear = dist2;
7015 entryNear = entry;
7016 }
7017 }
7018 else
7019 {
7020 foundNear = true;
7021 distNear = dist2;
7022 entryNear = entry;
7023 }
7024 }
7025 }
7026
7027 if (entryNear)
7028 return entryNear;
7029
7030 if (entryEntr)
7031 return entryEntr;
7032
7033 return entryFar;
7034}
7035
7037{
7038 GraveyardMapBounds range = GraveyardStore.equal_range(zoneId);
7039 for (; range.first != range.second; ++range.first)
7040 {
7041 GraveyardData const& data = range.first->second;
7042 if (data.safeLocId == id)
7043 return &data;
7044 }
7045 return nullptr;
7046}
7047
7049{
7050 AreaTriggerContainer::const_iterator itr = _areaTriggerStore.find(trigger);
7051 if (itr != _areaTriggerStore.end())
7052 return &itr->second;
7053 return nullptr;
7054}
7055
7057{
7058 AccessRequirementContainer::const_iterator itr = _accessRequirementStore.find(MAKE_PAIR32(mapid, difficulty));
7059 if (itr != _accessRequirementStore.end())
7060 return itr->second.get();
7061 return nullptr;
7062}
7063
7064bool ObjectMgr::AddGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= true*/)
7065{
7066 if (FindGraveyardData(id, zoneId))
7067 return false;
7068
7069 // add link to loaded data
7070 GraveyardData data;
7071 data.safeLocId = id;
7072 data.team = team;
7073
7074 GraveyardStore.insert(GraveyardContainer::value_type(zoneId, data));
7075
7076 // add link to DB
7077 if (persist)
7078 {
7080
7081 stmt->setUInt32(0, id);
7082 stmt->setUInt32(1, zoneId);
7083 stmt->setUInt16(2, uint16(team));
7084
7085 WorldDatabase.Execute(stmt);
7086 }
7087
7088 return true;
7089}
7090
7091void ObjectMgr::RemoveGraveyardLink(uint32 id, uint32 zoneId, uint32 team, bool persist /*= false*/)
7092{
7093 GraveyardMapBoundsNonConst range = GraveyardStore.equal_range(zoneId);
7094 if (range.first == range.second)
7095 {
7096 //TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone {} Team {} does not have a linked graveyard.", zoneId, team);
7097 return;
7098 }
7099
7100 bool found = false;
7101
7102 for (; range.first != range.second; ++range.first)
7103 {
7104 GraveyardData & data = range.first->second;
7105
7106 // skip not matching safezone id
7107 if (data.safeLocId != id)
7108 continue;
7109
7110 // skip enemy faction graveyard at same map (normal area, city, or battleground)
7111 // team == 0 case can be at call from .neargrave
7112 if (data.team != 0 && team != 0 && data.team != team)
7113 continue;
7114
7115 found = true;
7116 break;
7117 }
7118
7119 // no match, return
7120 if (!found)
7121 return;
7122
7123 // remove from links
7124 GraveyardStore.erase(range.first);
7125
7126 // remove link from DB
7127 if (persist)
7128 {
7130
7131 stmt->setUInt32(0, id);
7132 stmt->setUInt32(1, zoneId);
7133 stmt->setUInt16(2, uint16(team));
7134
7135 WorldDatabase.Execute(stmt);
7136 }
7137}
7138
7140{
7141 uint32 oldMSTime = getMSTime();
7142
7143 _areaTriggerStore.clear(); // need for reload case
7144
7145 // 0 1 2 3 4 5
7146 QueryResult result = WorldDatabase.Query("SELECT ID, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
7147 if (!result)
7148 {
7149 TC_LOG_INFO("server.loading", ">> Loaded 0 area trigger teleport definitions. DB table `areatrigger_teleport` is empty.");
7150 return;
7151 }
7152
7153 uint32 count = 0;
7154
7155 do
7156 {
7157 Field* fields = result->Fetch();
7158
7159 ++count;
7160
7161 uint32 Trigger_ID = fields[0].GetUInt32();
7162
7163 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
7164 if (!atEntry)
7165 {
7166 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
7167 continue;
7168 }
7169
7171
7172 at.target_mapId = fields[1].GetUInt16();
7173 at.target_X = fields[2].GetFloat();
7174 at.target_Y = fields[3].GetFloat();
7175 at.target_Z = fields[4].GetFloat();
7176 at.target_Orientation = fields[5].GetFloat();
7177
7178 MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
7179 if (!mapEntry)
7180 {
7181 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) target map (ID: {}) does not exist in `Map.dbc`.", Trigger_ID, at.target_mapId);
7182 continue;
7183 }
7184
7185 if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
7186 {
7187 TC_LOG_ERROR("sql.sql", "Area trigger (ID:{}) target coordinates not provided.", Trigger_ID);
7188 continue;
7189 }
7190
7191 _areaTriggerStore[Trigger_ID] = at;
7192
7193 } while (result->NextRow());
7194
7195 TC_LOG_INFO("server.loading", ">> Loaded {} area trigger teleport definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7196}
7197
7199{
7200 uint32 oldMSTime = getMSTime();
7201
7202 _accessRequirementStore.clear(); // need for reload case
7203
7204 // 0 1 2 3 4 5 6 7 8 9 10
7205 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");
7206
7207 if (!result)
7208 {
7209 TC_LOG_INFO("server.loading", ">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
7210 return;
7211 }
7212
7213 uint32 count = 0;
7214
7215 do
7216 {
7217 Field* fields = result->Fetch();
7218
7219 ++count;
7220
7221 uint32 mapid = fields[0].GetUInt32();
7222 uint8 difficulty = fields[1].GetUInt8();
7223 uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);
7224
7225 auto& ar = _accessRequirementStore[requirement_ID];
7226 ar = std::make_unique<AccessRequirement>();
7227
7228 ar->levelMin = fields[2].GetUInt8();
7229 ar->levelMax = fields[3].GetUInt8();
7230 ar->item_level = fields[4].GetUInt16();
7231 ar->item = fields[5].GetUInt32();
7232 ar->item2 = fields[6].GetUInt32();
7233 ar->quest_A = fields[7].GetUInt32();
7234 ar->quest_H = fields[8].GetUInt32();
7235 ar->achievement = fields[9].GetUInt32();
7236 ar->questFailedText = fields[10].GetString();
7237
7238 if (ar->item)
7239 {
7240 ItemTemplate const* pProto = GetItemTemplate(ar->item);
7241 if (!pProto)
7242 {
7243 TC_LOG_ERROR("misc", "Key item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item, mapid, difficulty);
7244 ar->item = 0;
7245 }
7246 }
7247
7248 if (ar->item2)
7249 {
7250 ItemTemplate const* pProto = GetItemTemplate(ar->item2);
7251 if (!pProto)
7252 {
7253 TC_LOG_ERROR("misc", "Second item {} does not exist for map {} difficulty {}, removing key requirement.", ar->item2, mapid, difficulty);
7254 ar->item2 = 0;
7255 }
7256 }
7257
7258 if (ar->quest_A)
7259 {
7260 if (!GetQuestTemplate(ar->quest_A))
7261 {
7262 TC_LOG_ERROR("sql.sql", "Required Alliance Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_A, mapid, difficulty);
7263 ar->quest_A = 0;
7264 }
7265 }
7266
7267 if (ar->quest_H)
7268 {
7269 if (!GetQuestTemplate(ar->quest_H))
7270 {
7271 TC_LOG_ERROR("sql.sql", "Required Horde Quest {} not exist for map {} difficulty {}, remove quest done requirement.", ar->quest_H, mapid, difficulty);
7272 ar->quest_H = 0;
7273 }
7274 }
7275
7276 if (ar->achievement)
7277 {
7278 if (!sAchievementStore.LookupEntry(ar->achievement))
7279 {
7280 TC_LOG_ERROR("sql.sql", "Required Achievement {} not exist for map {} difficulty {}, remove quest done requirement.", ar->achievement, mapid, difficulty);
7281 ar->achievement = 0;
7282 }
7283 }
7284 } while (result->NextRow());
7285
7286 TC_LOG_INFO("server.loading", ">> Loaded {} access requirement definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7287}
7288
7289/*
7290 * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
7291 */
7293{
7294 bool useParentDbValue = false;
7295 uint32 parentId = 0;
7296 MapEntry const* mapEntry = sMapStore.LookupEntry(Map);
7297 if (!mapEntry || mapEntry->CorpseMapID < 0)
7298 return nullptr;
7299
7300 if (mapEntry->IsDungeon())
7301 {
7302 InstanceTemplate const* iTemplate = sObjectMgr->GetInstanceTemplate(Map);
7303
7304 if (!iTemplate)
7305 return nullptr;
7306
7307 parentId = iTemplate->Parent;
7308 useParentDbValue = true;
7309 }
7310
7311 uint32 entrance_map = uint32(mapEntry->CorpseMapID);
7312 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7313 if ((!useParentDbValue && itr->second.target_mapId == entrance_map) || (useParentDbValue && itr->second.target_mapId == parentId))
7314 {
7315 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7316 if (atEntry && atEntry->ContinentID == Map)
7317 return &itr->second;
7318 }
7319 return nullptr;
7320}
7321
7326{
7327 for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr)
7328 {
7329 if (itr->second.target_mapId == Map)
7330 {
7331 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
7332 if (atEntry)
7333 return &itr->second;
7334 }
7335 }
7336 return nullptr;
7337}
7338
7340{
7341 QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
7342 if (result)
7343 GetGenerator<HighGuid::Player>().Set((*result)[0].GetUInt32() + 1);
7344
7345 result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
7346 if (result)
7347 GetGenerator<HighGuid::Item>().Set((*result)[0].GetUInt32() + 1);
7348
7349 // Cleanup other tables from nonexistent guids ( >= _hiItemGuid)
7350 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7351 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7352 CharacterDatabase.PExecute("DELETE a, ab FROM auctionhouse a LEFT JOIN auctionbidders ab ON ab.id = a.id WHERE itemguid >= '{}'",
7353 GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7354 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '{}'", GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed()); // One-time query
7355
7356 result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
7357 if (result)
7358 GetGenerator<HighGuid::Mo_Transport>().Set((*result)[0].GetUInt32() + 1);
7359
7360 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
7361 if (result)
7362 _auctionId = (*result)[0].GetUInt32()+1;
7363
7364 result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
7365 if (result)
7366 _mailId = (*result)[0].GetUInt32()+1;
7367
7368 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
7369 if (result)
7370 sArenaTeamMgr->SetNextArenaTeamId((*result)[0].GetUInt32()+1);
7371
7372 result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
7373 if (result)
7374 _equipmentSetGuid = (*result)[0].GetUInt64()+1;
7375
7376 result = CharacterDatabase.Query("SELECT MAX(guildId) FROM guild");
7377 if (result)
7378 sGuildMgr->SetNextGuildId((*result)[0].GetUInt32()+1);
7379
7380 result = CharacterDatabase.Query("SELECT MAX(guid) FROM `groups`");
7381 if (result)
7382 sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
7383
7384 result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
7385 if (result)
7386 _creatureSpawnId = (*result)[0].GetUInt32() + 1;
7387
7388 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
7389 if (result)
7390 _gameObjectSpawnId = (*result)[0].GetUInt32() + 1;
7391}
7392
7394{
7395 auto itr = _guidGenerators.find(high);
7396 if (itr == _guidGenerators.end())
7397 itr = _guidGenerators.insert(std::make_pair(high, std::make_unique<ObjectGuidGenerator>(high))).first;
7398
7399 return *itr->second;
7400}
7401
7403{
7404 if (_auctionId >= 0xFFFFFFFE)
7405 {
7406 TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7408 }
7409 return _auctionId++;
7410}
7411
7413{
7414 if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL))
7415 {
7416 TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7418 }
7419 return _equipmentSetGuid++;
7420}
7421
7423{
7424 if (_mailId >= 0xFFFFFFFE)
7425 {
7426 TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7428 }
7429 return _mailId++;
7430}
7431
7433{
7434 if (_hiPetNumber >= 0xFFFFFFFE)
7435 {
7436 TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7438 }
7439 return _hiPetNumber++;
7440}
7441
7443{
7444 if (_creatureSpawnId >= uint32(0xFFFFFF))
7445 {
7446 TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info.");
7448 }
7449 return _creatureSpawnId++;
7450}
7451
7453{
7454 if (_gameObjectSpawnId >= uint32(0xFFFFFF))
7455 {
7456 TC_LOG_ERROR("misc", "GameObject spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. ");
7458 }
7459 return _gameObjectSpawnId++;
7460}
7461
7463{
7464 uint32 oldMSTime = getMSTime();
7465
7466 _gameObjectLocaleStore.clear(); // need for reload case
7467
7468 // 0 1 2 3
7469 QueryResult result = WorldDatabase.Query("SELECT entry, locale, name, castBarCaption FROM gameobject_template_locale");
7470 if (!result)
7471 return;
7472
7473 do
7474 {
7475 Field* fields = result->Fetch();
7476
7477 uint32 id = fields[0].GetUInt32();
7478 std::string localeName = fields[1].GetString();
7479
7480 LocaleConstant locale = GetLocaleByName(localeName);
7481 if (locale == LOCALE_enUS)
7482 continue;
7483
7485 AddLocaleString(fields[2].GetString(), locale, data.Name);
7486 AddLocaleString(fields[3].GetString(), locale, data.CastBarCaption);
7487 } while (result->NextRow());
7488
7489 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject_template_locale strings in {} ms", uint32(_gameObjectLocaleStore.size()), GetMSTimeDiffToNow(oldMSTime));
7490}
7491
7492inline void CheckGOLockId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7493{
7494 if (sLockStore.LookupEntry(dataN))
7495 return;
7496
7497 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but lock (Id: {}) not found.",
7498 goInfo->entry, goInfo->type, N, goInfo->door.lockId, goInfo->door.lockId);
7499}
7500
7501inline void CheckGOLinkedTrapId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7502{
7503 if (GameObjectTemplate const* trapInfo = sObjectMgr->GetGameObjectTemplate(dataN))
7504 {
7505 if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
7506 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but GO (Entry {}) have not GAMEOBJECT_TYPE_TRAP ({}) type.",
7507 goInfo->entry, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
7508 }
7509}
7510
7511inline void CheckGOSpellId(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7512{
7513 if (sSpellMgr->GetSpellInfo(dataN))
7514 return;
7515
7516 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but Spell (Entry {}) not exist.",
7517 goInfo->entry, goInfo->type, N, dataN, dataN);
7518}
7519
7520inline void CheckAndFixGOChairHeightId(GameObjectTemplate const* goInfo, uint32& dataN, uint32 N)
7521{
7523 return;
7524
7525 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but correct chair height in range 0..{}.",
7527
7528 // prevent client and server unexpected work
7529 dataN = 0;
7530}
7531
7533{
7534 // 0/1 correct values
7535 if (dataN <= 1)
7536 return;
7537
7538 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) noDamageImmune field value.", goTemplate->entry, goTemplate->type, N, dataN);
7539}
7540
7541inline void CheckGOConsumable(GameObjectTemplate const* goInfo, uint32 dataN, uint32 N)
7542{
7543 // 0/1 correct values
7544 if (dataN <= 1)
7545 return;
7546
7547 TC_LOG_ERROR("sql.sql", "Gameobject (Entry: {} GoType: {}) have data{}={} but expected boolean (0/1) consumable field value.",
7548 goInfo->entry, goInfo->type, N, dataN);
7549}
7550
7552{
7553 uint32 oldMSTime = getMSTime();
7554
7555 // 0 1 2 3 4 5 6 7
7556 QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, "
7557 // 8 9 10 11 12 13 14 15 16 17 18 19 20
7558 "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, "
7559 // 21 22 23 24 25 26 27 28 29 30 31 32 33 34
7560 "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, AIName, ScriptName, StringId "
7561 "FROM gameobject_template");
7562
7563 if (!result)
7564 {
7565 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject definitions. DB table `gameobject_template` is empty.");
7566 return;
7567 }
7568
7569 _gameObjectTemplateStore.reserve(result->GetRowCount());
7570 do
7571 {
7572 Field* fields = result->Fetch();
7573
7574 uint32 entry = fields[0].GetUInt32();
7575
7577 got.entry = entry;
7578 got.type = uint32(fields[1].GetUInt8());
7579 got.displayId = fields[2].GetUInt32();
7580 got.name = fields[3].GetString();
7581 got.IconName = fields[4].GetString();
7582 got.castBarCaption = fields[5].GetString();
7583 got.unk1 = fields[6].GetString();
7584 got.size = fields[7].GetFloat();
7585
7586 for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i)
7587 got.raw.data[i] = fields[8 + i].GetInt32(); // data1 and data6 can be -1
7588
7589 got.AIName = fields[32].GetString();
7590 got.ScriptId = GetScriptId(fields[33].GetString());
7591 got.StringId = fields[34].GetString();
7592
7593 // Checks
7594 if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
7595 {
7596 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has non-registered `AIName` '{}' set, removing", got.entry, got.AIName);
7597 got.AIName.clear();
7598 }
7599
7600 switch (got.type)
7601 {
7602 case GAMEOBJECT_TYPE_DOOR: //0
7603 {
7604 if (got.door.lockId)
7605 CheckGOLockId(&got, got.door.lockId, 1);
7607 break;
7608 }
7609 case GAMEOBJECT_TYPE_BUTTON: //1
7610 {
7611 if (got.button.lockId)
7612 CheckGOLockId(&got, got.button.lockId, 1);
7614 break;
7615 }
7617 {
7618 if (got.questgiver.lockId)
7619 CheckGOLockId(&got, got.questgiver.lockId, 0);
7621 break;
7622 }
7623 case GAMEOBJECT_TYPE_CHEST: //3
7624 {
7625 if (got.chest.lockId)
7626 CheckGOLockId(&got, got.chest.lockId, 0);
7627
7628 CheckGOConsumable(&got, got.chest.consumable, 3);
7629
7630 if (got.chest.linkedTrapId) // linked trap
7632 break;
7633 }
7634 case GAMEOBJECT_TYPE_TRAP: //6
7635 {
7636 if (got.trap.lockId)
7637 CheckGOLockId(&got, got.trap.lockId, 0);
7638 break;
7639 }
7640 case GAMEOBJECT_TYPE_CHAIR: //7
7642 break;
7644 {
7645 if (got.spellFocus.focusId)
7646 {
7647 if (!sSpellFocusObjectStore.LookupEntry(got.spellFocus.focusId))
7648 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but SpellFocus (Id: {}) not exist.",
7649 entry, got.type, got.spellFocus.focusId, got.spellFocus.focusId);
7650 }
7651
7652 if (got.spellFocus.linkedTrapId) // linked trap
7654 break;
7655 }
7656 case GAMEOBJECT_TYPE_GOOBER: //10
7657 {
7658 if (got.goober.lockId)
7659 CheckGOLockId(&got, got.goober.lockId, 0);
7660
7661 CheckGOConsumable(&got, got.goober.consumable, 3);
7662
7663 if (got.goober.pageId) // pageId
7664 {
7665 if (!GetPageText(got.goober.pageId))
7666 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data7={} but PageText (Entry {}) not exist.",
7667 entry, got.type, got.goober.pageId, got.goober.pageId);
7668 }
7670 if (got.goober.linkedTrapId) // linked trap
7672 break;
7673 }
7675 {
7676 if (got.areadamage.lockId)
7677 CheckGOLockId(&got, got.areadamage.lockId, 0);
7678 break;
7679 }
7680 case GAMEOBJECT_TYPE_CAMERA: //13
7681 {
7682 if (got.camera.lockId)
7683 CheckGOLockId(&got, got.camera.lockId, 0);
7684 break;
7685 }
7687 {
7688 if (got.moTransport.taxiPathId)
7689 {
7691 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {} GoType: {}) have data0={} but TaxiPath (Id: {}) not exist.",
7692 entry, got.type, got.moTransport.taxiPathId, got.moTransport.taxiPathId);
7693 }
7694 if (uint32 transportMap = got.moTransport.mapID)
7695 _transportMaps.insert(transportMap);
7696 break;
7697 }
7698 case GAMEOBJECT_TYPE_RITUAL: //18
7699 break;
7701 {
7702 // always must have spell
7703 CheckGOSpellId(&got, got.spellcaster.spellId, 0);
7704 break;
7705 }
7706 case GAMEOBJECT_TYPE_FLAGSTAND: //24
7707 {
7708 if (got.flagstand.lockId)
7709 CheckGOLockId(&got, got.flagstand.lockId, 0);
7711 break;
7712 }
7714 {
7715 if (got.fishinghole.lockId)
7716 CheckGOLockId(&got, got.fishinghole.lockId, 4);
7717 break;
7718 }
7719 case GAMEOBJECT_TYPE_FLAGDROP: //26
7720 {
7721 if (got.flagdrop.lockId)
7722 CheckGOLockId(&got, got.flagdrop.lockId, 0);
7724 break;
7725 }
7728 break;
7729 }
7730 } while (result->NextRow());
7731
7732 TC_LOG_INFO("server.loading", ">> Loaded {} game object templates in {} ms", _gameObjectTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime));
7733}
7734
7736{
7737 uint32 oldMSTime = getMSTime();
7738
7739 // 0 1 2 3 4 5 6 7 8
7740 QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3 FROM gameobject_template_addon");
7741
7742 if (!result)
7743 {
7744 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty.");
7745 return;
7746 }
7747
7748 uint32 count = 0;
7749 do
7750 {
7751 Field* fields = result->Fetch();
7752
7753 uint32 entry = fields[0].GetUInt32();
7754
7755 GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry);
7756 if (!got)
7757 {
7758 TC_LOG_ERROR("sql.sql", "GameObject template (Entry: {}) does not exist but has a record in `gameobject_template_addon`", entry);
7759 continue;
7760 }
7761
7763 gameObjectAddon.Faction = uint32(fields[1].GetUInt16());
7764 gameObjectAddon.Flags = fields[2].GetUInt32();
7765 gameObjectAddon.Mingold = fields[3].GetUInt32();
7766 gameObjectAddon.Maxgold = fields[4].GetUInt32();
7767
7768 for (uint32 i = 0; i < gameObjectAddon.artKits.size(); ++i)
7769 {
7770 uint32 artKitID = fields[5 + i].GetUInt32();
7771 if (!artKitID)
7772 continue;
7773
7774 if (!sGameObjectArtKitStore.LookupEntry(artKitID))
7775 {
7776 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid `artkit{}` ({}) defined, set to zero instead.", entry, i, artKitID);
7777 continue;
7778 }
7779
7780 gameObjectAddon.artKits[i] = artKitID;
7781 }
7782
7783 // checks
7784 if (gameObjectAddon.Faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.Faction))
7785 TC_LOG_ERROR("sql.sql", "GameObject (Entry: {}) has invalid faction ({}) defined in `gameobject_template_addon`.", entry, gameObjectAddon.Faction);
7786
7787 if (gameObjectAddon.Maxgold > 0)
7788 {
7789 switch (got->type)
7790 {
7793 break;
7794 default:
7795 TC_LOG_ERROR("sql.sql", "GameObject (Entry {} GoType: {}) cannot be looted but has maxgold set in `gameobject_template_addon`.", entry, got->type);
7796 break;
7797 }
7798 }
7799
7800 ++count;
7801 }
7802 while (result->NextRow());
7803
7804 TC_LOG_INFO("server.loading", ">> Loaded {} game object template addons in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7805}
7806
7808{
7809 uint32 oldMSTime = getMSTime();
7810
7811 // 0 1 2
7812 QueryResult result = WorldDatabase.Query("SELECT spawnId, faction, flags FROM gameobject_overrides");
7813 if (!result)
7814 {
7815 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject faction and flags overrides. DB table `gameobject_overrides` is empty.");
7816 return;
7817 }
7818
7819 uint32 count = 0;
7820 do
7821 {
7822 Field* fields = result->Fetch();
7823
7824 ObjectGuid::LowType spawnId = fields[0].GetUInt32();
7825 GameObjectData const* goData = GetGameObjectData(spawnId);
7826 if (!goData)
7827 {
7828 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) does not exist but has a record in `gameobject_overrides`", spawnId);
7829 continue;
7830 }
7831
7832 GameObjectOverride& gameObjectOverride = _gameObjectOverrideStore[spawnId];
7833 gameObjectOverride.Faction = fields[1].GetUInt16();
7834 gameObjectOverride.Flags = fields[2].GetUInt32();
7835
7836 if (gameObjectOverride.Faction && !sFactionTemplateStore.LookupEntry(gameObjectOverride.Faction))
7837 TC_LOG_ERROR("sql.sql", "GameObject (SpawnId: {}) has invalid faction ({}) defined in `gameobject_overrides`.", spawnId, gameObjectOverride.Faction);
7838
7839 ++count;
7840 } while (result->NextRow());
7841
7842 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject faction and flags overrides in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7843}
7844
7846{
7847 uint32 oldMSTime = getMSTime();
7848
7849 QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");
7850
7851 if (!result)
7852 {
7853 TC_LOG_INFO("server.loading", ">> Loaded 0 BaseXP definitions. DB table `exploration_basexp` is empty.");
7854 return;
7855 }
7856
7857 uint32 count = 0;
7858
7859 do
7860 {
7861 Field* fields = result->Fetch();
7862 uint8 level = fields[0].GetUInt8();
7863 uint32 basexp = fields[1].GetInt32();
7864 _baseXPTable[level] = basexp;
7865 ++count;
7866 }
7867 while (result->NextRow());
7868
7869 TC_LOG_INFO("server.loading", ">> Loaded {} BaseXP definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7870}
7871
7873{
7874 return _baseXPTable[level] ? _baseXPTable[level] : 0;
7875}
7876
7878{
7879 if (level < _playerXPperLevel.size())
7880 return _playerXPperLevel[level];
7881 return 0;
7882}
7883
7885{
7886 uint32 oldMSTime = getMSTime();
7887 // 0 1 2
7888 QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");
7889
7890 if (!result)
7891 {
7892 TC_LOG_INFO("server.loading", ">> Loaded 0 pet name parts. DB table `pet_name_generation` is empty!");
7893 return;
7894 }
7895
7896 uint32 count = 0;
7897
7898 do
7899 {
7900 Field* fields = result->Fetch();
7901 std::string word = fields[0].GetString();
7902 uint32 entry = fields[1].GetUInt32();
7903 bool half = fields[2].GetBool();
7904 if (half)
7905 _petHalfName1[entry].push_back(word);
7906 else
7907 _petHalfName0[entry].push_back(word);
7908 ++count;
7909 }
7910 while (result->NextRow());
7911
7912 TC_LOG_INFO("server.loading", ">> Loaded {} pet name parts in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
7913}
7914
7916{
7917 uint32 oldMSTime = getMSTime();
7918
7919 QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
7920 if (result)
7921 {
7922 Field* fields = result->Fetch();
7923 _hiPetNumber = fields[0].GetUInt32()+1;
7924 }
7925
7926 TC_LOG_INFO("server.loading", ">> Loaded the max pet number: {} in {} ms", _hiPetNumber-1, GetMSTimeDiffToNow(oldMSTime));
7927}
7928
7930{
7931 std::vector<std::string>& list0 = _petHalfName0[entry];
7932 std::vector<std::string>& list1 = _petHalfName1[entry];
7933
7934 if (list0.empty() || list1.empty())
7935 {
7936 CreatureTemplate const* cinfo = GetCreatureTemplate(entry);
7937 if (!cinfo)
7938 return std::string();
7939
7940 char const* petname = GetPetName(cinfo->family, sWorld->GetDefaultDbcLocale());
7941 if (petname)
7942 return std::string(petname);
7943 else
7944 return cinfo->Name;
7945 }
7946
7947 return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
7948}
7949
7951{
7952 uint32 oldMSTime = getMSTime();
7953
7954 _repRewardRateStore.clear(); // for reload case
7955
7956 uint32 count = 0; // 0 1 2 3 4 5 6 7
7957 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");
7958 if (!result)
7959 {
7960 TC_LOG_INFO("server.loading", ">> Loaded `reputation_reward_rate`, table is empty!");
7961 return;
7962 }
7963
7964 do
7965 {
7966 Field* fields = result->Fetch();
7967
7968 uint32 factionId = fields[0].GetUInt32();
7969
7970 RepRewardRate repRate;
7971
7972 repRate.questRate = fields[1].GetFloat();
7973 repRate.questDailyRate = fields[2].GetFloat();
7974 repRate.questWeeklyRate = fields[3].GetFloat();
7975 repRate.questMonthlyRate = fields[4].GetFloat();
7976 repRate.questRepeatableRate = fields[5].GetFloat();
7977 repRate.creatureRate = fields[6].GetFloat();
7978 repRate.spellRate = fields[7].GetFloat();
7979
7980 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
7981 if (!factionEntry)
7982 {
7983 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_reward_rate`", factionId);
7984 continue;
7985 }
7986
7987 if (repRate.questRate < 0.0f)
7988 {
7989 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_rate with invalid rate {}, skipping data for faction {}", repRate.questRate, factionId);
7990 continue;
7991 }
7992
7993 if (repRate.questDailyRate < 0.0f)
7994 {
7995 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_daily_rate with invalid rate {}, skipping data for faction {}", repRate.questDailyRate, factionId);
7996 continue;
7997 }
7998
7999 if (repRate.questWeeklyRate < 0.0f)
8000 {
8001 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_weekly_rate with invalid rate {}, skipping data for faction {}", repRate.questWeeklyRate, factionId);
8002 continue;
8003 }
8004
8005 if (repRate.questMonthlyRate < 0.0f)
8006 {
8007 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_monthly_rate with invalid rate {}, skipping data for faction {}", repRate.questMonthlyRate, factionId);
8008 continue;
8009 }
8010
8011 if (repRate.questRepeatableRate < 0.0f)
8012 {
8013 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has quest_repeatable_rate with invalid rate {}, skipping data for faction {}", repRate.questRepeatableRate, factionId);
8014 continue;
8015 }
8016
8017 if (repRate.creatureRate < 0.0f)
8018 {
8019 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has creature_rate with invalid rate {}, skipping data for faction {}", repRate.creatureRate, factionId);
8020 continue;
8021 }
8022
8023 if (repRate.spellRate < 0.0f)
8024 {
8025 TC_LOG_ERROR("sql.sql", "Table reputation_reward_rate has spell_rate with invalid rate {}, skipping data for faction {}", repRate.spellRate, factionId);
8026 continue;
8027 }
8028
8029 _repRewardRateStore[factionId] = repRate;
8030
8031 ++count;
8032 }
8033 while (result->NextRow());
8034
8035 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_reward_rate in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8036}
8037
8039{
8040 uint32 oldMSTime = getMSTime();
8041
8042 // For reload case
8043 _repOnKillStore.clear();
8044
8045 uint32 count = 0;
8046
8047 // 0 1 2
8048 QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
8049 // 3 4 5 6 7 8 9
8050 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
8051 "FROM creature_onkill_reputation");
8052
8053 if (!result)
8054 {
8055 TC_LOG_INFO("server.loading", ">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
8056 return;
8057 }
8058
8059 do
8060 {
8061 Field* fields = result->Fetch();
8062
8063 uint32 creature_id = fields[0].GetUInt32();
8064
8065 ReputationOnKillEntry repOnKill;
8066 repOnKill.RepFaction1 = fields[1].GetInt16();
8067 repOnKill.RepFaction2 = fields[2].GetInt16();
8068 repOnKill.IsTeamAward1 = fields[3].GetBool();
8069 repOnKill.ReputationMaxCap1 = fields[4].GetUInt8();
8070 repOnKill.RepValue1 = fields[5].GetInt32();
8071 repOnKill.IsTeamAward2 = fields[6].GetBool();
8072 repOnKill.ReputationMaxCap2 = fields[7].GetUInt8();
8073 repOnKill.RepValue2 = fields[8].GetInt32();
8074 repOnKill.TeamDependent = fields[9].GetBool();
8075
8076 if (!GetCreatureTemplate(creature_id))
8077 {
8078 TC_LOG_ERROR("sql.sql", "Table `creature_onkill_reputation` has data for nonexistent creature entry ({}), skipped", creature_id);
8079 continue;
8080 }
8081
8082 if (repOnKill.RepFaction1)
8083 {
8084 FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(repOnKill.RepFaction1);
8085 if (!factionEntry1)
8086 {
8087 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction1);
8088 continue;
8089 }
8090 }
8091
8092 if (repOnKill.RepFaction2)
8093 {
8094 FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(repOnKill.RepFaction2);
8095 if (!factionEntry2)
8096 {
8097 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `creature_onkill_reputation`", repOnKill.RepFaction2);
8098 continue;
8099 }
8100 }
8101
8102 _repOnKillStore[creature_id] = repOnKill;
8103
8104 ++count;
8105 } while (result->NextRow());
8106
8107 TC_LOG_INFO("server.loading", ">> Loaded {} creature award reputation definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8108}
8109
8111{
8112 uint32 oldMSTime = getMSTime();
8113
8114 _repSpilloverTemplateStore.clear(); // for reload case
8115
8116 uint32 count = 0; // 0 1 2 3 4 5 6 7 8 9 10 11 12
8117 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");
8118
8119 if (!result)
8120 {
8121 TC_LOG_INFO("server.loading", ">> Loaded `reputation_spillover_template`, table is empty.");
8122 return;
8123 }
8124
8125 do
8126 {
8127 Field* fields = result->Fetch();
8128
8129 uint32 factionId = fields[0].GetUInt16();
8130
8131 RepSpilloverTemplate repTemplate;
8132
8133 repTemplate.faction[0] = fields[1].GetUInt16();
8134 repTemplate.faction_rate[0] = fields[2].GetFloat();
8135 repTemplate.faction_rank[0] = fields[3].GetUInt8();
8136 repTemplate.faction[1] = fields[4].GetUInt16();
8137 repTemplate.faction_rate[1] = fields[5].GetFloat();
8138 repTemplate.faction_rank[1] = fields[6].GetUInt8();
8139 repTemplate.faction[2] = fields[7].GetUInt16();
8140 repTemplate.faction_rate[2] = fields[8].GetFloat();
8141 repTemplate.faction_rank[2] = fields[9].GetUInt8();
8142 repTemplate.faction[3] = fields[10].GetUInt16();
8143 repTemplate.faction_rate[3] = fields[11].GetFloat();
8144 repTemplate.faction_rank[3] = fields[12].GetUInt8();
8145
8146 FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
8147
8148 if (!factionEntry)
8149 {
8150 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} does not exist but is used in `reputation_spillover_template`", factionId);
8151 continue;
8152 }
8153
8154 if (factionEntry->ParentFactionID == 0)
8155 {
8156 TC_LOG_ERROR("sql.sql", "Faction (faction.dbc) {} in `reputation_spillover_template` does not belong to any team, skipping", factionId);
8157 continue;
8158 }
8159
8160 bool invalidSpilloverFaction = false;
8161 for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
8162 {
8163 if (repTemplate.faction[i])
8164 {
8165 FactionEntry const* factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);
8166
8167 if (!factionSpillover)
8168 {
8169 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);
8170 invalidSpilloverFaction = true;
8171 break;
8172 }
8173
8174 if (factionSpillover->ReputationIndex < 0)
8175 {
8176 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);
8177 invalidSpilloverFaction = true;
8178 break;
8179 }
8180
8181 if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
8182 {
8183 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]);
8184 invalidSpilloverFaction = true;
8185 break;
8186 }
8187 }
8188 }
8189
8190 if (invalidSpilloverFaction)
8191 continue;
8192
8193 _repSpilloverTemplateStore[factionId] = repTemplate;
8194
8195 ++count;
8196 }
8197 while (result->NextRow());
8198
8199 TC_LOG_INFO("server.loading", ">> Loaded {} reputation_spillover_template in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8200}
8201
8203{
8204 uint32 oldMSTime = getMSTime();
8205
8206 _pointsOfInterestStore.clear(); // need for reload case
8207
8208 uint32 count = 0;
8209
8210 // 0 1 2 3 4 5 6
8211 QueryResult result = WorldDatabase.Query("SELECT ID, PositionX, PositionY, Icon, Flags, Importance, Name FROM points_of_interest");
8212
8213 if (!result)
8214 {
8215 TC_LOG_INFO("server.loading", ">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
8216 return;
8217 }
8218
8219 do
8220 {
8221 Field* fields = result->Fetch();
8222
8223 uint32 id = fields[0].GetUInt32();
8224
8225 PointOfInterest pointOfInterest;
8226 pointOfInterest.ID = id;
8227 pointOfInterest.PositionX = fields[1].GetFloat();
8228 pointOfInterest.PositionY = fields[2].GetFloat();
8229 pointOfInterest.Icon = fields[3].GetUInt32();
8230 pointOfInterest.Flags = fields[4].GetUInt32();
8231 pointOfInterest.Importance = fields[5].GetUInt32();
8232 pointOfInterest.Name = fields[6].GetString();
8233
8234 if (!Trinity::IsValidMapCoord(pointOfInterest.PositionX, pointOfInterest.PositionY))
8235 {
8236 TC_LOG_ERROR("sql.sql", "Table `points_of_interest` (ID: {}) have invalid coordinates (X: {} Y: {}), ignored.", id, pointOfInterest.PositionX, pointOfInterest.PositionY);
8237 continue;
8238 }
8239
8240 _pointsOfInterestStore[id] = pointOfInterest;
8241
8242 ++count;
8243 } while (result->NextRow());
8244
8245 TC_LOG_INFO("server.loading", ">> Loaded {} Points of Interest definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8246}
8247
8249{
8250 uint32 oldMSTime = getMSTime();
8251
8252 _questPOIStore.clear(); // need for reload case
8253
8254 // 0 1 2 3 4 5 6 7
8255 QueryResult result = WorldDatabase.Query("SELECT QuestID, id, ObjectiveIndex, MapID, WorldMapAreaId, Floor, Priority, Flags FROM quest_poi");
8256 if (!result)
8257 {
8258 TC_LOG_INFO("server.loading", ">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
8259 return;
8260 }
8261
8262 _questPOIStore.reserve(result->GetRowCount());
8263
8264 // 0 1 2 3
8265 QueryResult points = WorldDatabase.Query("SELECT QuestID, Idx1, X, Y FROM quest_poi_points ORDER BY QuestID DESC, Idx2");
8266
8267 std::vector<std::vector<std::vector<QuestPOIBlobPoint>>> POIs;
8268 if (points)
8269 {
8270 // The first result should have the highest questId
8271 Field* fields = points->Fetch();
8272 uint32 const maxQuestPOIId = fields[0].GetUInt32();
8273 POIs.resize(maxQuestPOIId + 1);
8274
8275 do
8276 {
8277 fields = points->Fetch();
8278
8279 uint32 questId = fields[0].GetUInt32();
8280 uint32 id = fields[1].GetUInt32();
8281 int32 x = fields[2].GetInt32();
8282 int32 y = fields[3].GetInt32();
8283
8284 if (POIs[questId].size() <= id + 1)
8285 POIs[questId].resize(id + 10);
8286
8287 QuestPOIBlobPoint point;
8288 point.X = x;
8289 point.Y = y;
8290
8291 POIs[questId][id].push_back(point);
8292 } while (points->NextRow());
8293 }
8294
8295 do
8296 {
8297 Field* fields = result->Fetch();
8298
8299 uint32 questId = fields[0].GetUInt32();
8300 uint32 id = fields[1].GetUInt32();
8301 int32 objIndex = fields[2].GetInt32();
8302 uint32 mapId = fields[3].GetUInt32();
8303 uint32 WorldMapAreaId = fields[4].GetUInt32();
8304 uint32 FloorId = fields[5].GetUInt32();
8305 uint32 unk3 = fields[6].GetUInt32();
8306 uint32 unk4 = fields[7].GetUInt32();
8307
8308 QuestPOIBlobData POI;
8309 POI.BlobIndex = id;
8310 POI.ObjectiveIndex = objIndex;
8311 POI.MapID = mapId;
8312 POI.WorldMapAreaID = WorldMapAreaId;
8313 POI.Floor = FloorId;
8314 POI.Unk3 = unk3;
8315 POI.Unk4 = unk4;
8316
8317 if (questId < POIs.size() && id < POIs[questId].size())
8318 {
8319 POI.QuestPOIBlobPointStats = POIs[questId][id];
8320
8321 auto itr = _questPOIStore.find(questId);
8322 if (itr == _questPOIStore.end())
8323 {
8324 QuestPOIWrapper wrapper;
8325 QuestPOIData data;
8326 data.QuestID = questId;
8327 wrapper.POIData = data;
8328
8329 std::tie(itr, std::ignore) = _questPOIStore.emplace(questId, std::move(wrapper));
8330 }
8331
8332 itr->second.POIData.QuestPOIBlobDataStats.push_back(POI);
8333 }
8334 else
8335 TC_LOG_ERROR("sql.sql", "Table quest_poi references unknown quest points for quest {} POI id {}", questId, id);
8336 } while (result->NextRow());
8337
8338 TC_LOG_INFO("server.loading", ">> Loaded {} quest POI definitions in {} ms", _questPOIStore.size(), GetMSTimeDiffToNow(oldMSTime));
8339}
8340
8342{
8343 uint32 oldMSTime = getMSTime();
8344
8345 _spellClickInfoStore.clear();
8346 // 0 1 2 3
8347 QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, cast_flags, user_type FROM npc_spellclick_spells");
8348
8349 if (!result)
8350 {
8351 TC_LOG_INFO("server.loading", ">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
8352 return;
8353 }
8354
8355 uint32 count = 0;
8356
8357 do
8358 {
8359 Field* fields = result->Fetch();
8360
8361 uint32 npc_entry = fields[0].GetUInt32();
8362 CreatureTemplate const* cInfo = GetCreatureTemplate(npc_entry);
8363 if (!cInfo)
8364 {
8365 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells references unknown creature_template {}. Skipping entry.", npc_entry);
8366 continue;
8367 }
8368
8369 uint32 spellid = fields[1].GetUInt32();
8370 SpellInfo const* spellinfo = sSpellMgr->GetSpellInfo(spellid);
8371 if (!spellinfo)
8372 {
8373 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown spellid {}. Skipping entry.", npc_entry, spellid);
8374 continue;
8375 }
8376
8377 uint8 userType = fields[3].GetUInt16();
8378 if (userType >= SPELL_CLICK_USER_MAX)
8379 TC_LOG_ERROR("sql.sql", "Table npc_spellclick_spells creature: {} references unknown user type {}. Skipping entry.", npc_entry, uint32(userType));
8380
8381 uint8 castFlags = fields[2].GetUInt8();
8382 SpellClickInfo info;
8383 info.spellId = spellid;
8384 info.castFlags = castFlags;
8385 info.userType = SpellClickUserTypes(userType);
8386 _spellClickInfoStore.insert(SpellClickInfoContainer::value_type(npc_entry, info));
8387
8388 ++count;
8389 }
8390 while (result->NextRow());
8391
8392 // all spellclick data loaded, now we check if there are creatures with NPC_FLAG_SPELLCLICK but with no data
8393 // NOTE: It *CAN* be the other way around: no spellclick flag but with spellclick data, in case of creature-only vehicle accessories
8394 for (auto& creatureTemplatePair : _creatureTemplateStore)
8395 {
8396 if ((creatureTemplatePair.second.npcflag & UNIT_NPC_FLAG_SPELLCLICK) && !_spellClickInfoStore.count(creatureTemplatePair.first))
8397 {
8398 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);
8399 creatureTemplatePair.second.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
8400 }
8401 }
8402
8403 TC_LOG_INFO("server.loading", ">> Loaded {} spellclick definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8404}
8405
8407{
8408 // remove mapid*cellid -> guid_set map
8409 CreatureData const* data = GetCreatureData(guid);
8410 if (data)
8411 {
8412 RemoveCreatureFromGrid(guid, data);
8413 OnDeleteSpawnData(data);
8414 }
8415
8416 _creatureDataStore.erase(guid);
8417}
8418
8420{
8421 // remove mapid*cellid -> guid_set map
8422 GameObjectData const* data = GetGameObjectData(guid);
8423 if (data)
8424 {
8425 RemoveGameobjectFromGrid(guid, data);
8426 OnDeleteSpawnData(data);
8427 }
8428
8429 _gameObjectDataStore.erase(guid);
8430}
8431
8432void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, std::string const& table)
8433{
8434 uint32 oldMSTime = getMSTime();
8435
8436 map.clear(); // need for reload case
8437
8438 uint32 count = 0;
8439
8440 QueryResult result = WorldDatabase.PQuery("SELECT id, quest FROM {}", table);
8441
8442 if (!result)
8443 {
8444 TC_LOG_INFO("server.loading", ">> Loaded 0 quest relations from `{}`, table is empty.", table);
8445 return;
8446 }
8447
8448 do
8449 {
8450 uint32 id = result->Fetch()[0].GetUInt32();
8451 uint32 quest = result->Fetch()[1].GetUInt32();
8452
8453 if (!_questTemplates.count(quest))
8454 {
8455 TC_LOG_ERROR("sql.sql", "Table `{}`: Quest {} listed for entry {} does not exist.", table, quest, id);
8456 continue;
8457 }
8458
8459 map.insert(QuestRelations::value_type(id, quest));
8460 ++count;
8461 } while (result->NextRow());
8462
8463 TC_LOG_INFO("server.loading", ">> Loaded {} quest relations from {} in {} ms", count, table, GetMSTimeDiffToNow(oldMSTime));
8464}
8465
8467{
8468 LoadQuestRelationsHelper(_goQuestRelations, "gameobject_queststarter");
8469
8470 for (QuestRelations::iterator itr = _goQuestRelations.begin(); itr != _goQuestRelations.end(); ++itr)
8471 {
8472 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8473 if (!goInfo)
8474 TC_LOG_ERROR("sql.sql", "Table `gameobject_queststarter` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8475 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8476 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);
8477 }
8478}
8479
8481{
8482 LoadQuestRelationsHelper(_goQuestInvolvedRelations, "gameobject_questender");
8483
8484 for (QuestRelations::iterator itr = _goQuestInvolvedRelations.begin(); itr != _goQuestInvolvedRelations.end(); ++itr)
8485 {
8486 GameObjectTemplate const* goInfo = GetGameObjectTemplate(itr->first);
8487 if (!goInfo)
8488 TC_LOG_ERROR("sql.sql", "Table `gameobject_questender` has data for nonexistent gameobject entry ({}) and existed quest {}", itr->first, itr->second);
8489 else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
8490 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);
8491 }
8492}
8493
8495{
8496 LoadQuestRelationsHelper(_creatureQuestRelations, "creature_queststarter");
8497
8498 for (QuestRelations::iterator itr = _creatureQuestRelations.begin(); itr != _creatureQuestRelations.end(); ++itr)
8499 {
8500 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8501 if (!cInfo)
8502 TC_LOG_ERROR("sql.sql", "Table `creature_queststarter` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8503 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8504 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);
8505 }
8506}
8507
8509{
8511
8512 for (QuestRelations::iterator itr = _creatureQuestInvolvedRelations.begin(); itr != _creatureQuestInvolvedRelations.end(); ++itr)
8513 {
8514 CreatureTemplate const* cInfo = GetCreatureTemplate(itr->first);
8515 if (!cInfo)
8516 TC_LOG_ERROR("sql.sql", "Table `creature_questender` has data for nonexistent creature entry ({}) and existed quest {}", itr->first, itr->second);
8517 else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
8518 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);
8519 }
8520}
8521
8523{
8524 while ((_it != _end) && !Quest::IsTakingQuestEnabled(_it->second))
8525 ++_it;
8526}
8527
8529{
8530 return (std::find_if(_begin, _end, [questId](QuestRelations::value_type const& pair) { return (pair.second == questId); }) != _end) && (!_onlyActive || Quest::IsTakingQuestEnabled(questId));
8531}
8532
8534{
8535 uint32 oldMSTime = getMSTime();
8536
8537 _reservedNamesStore.clear(); // need for reload case
8538
8539 QueryResult result = CharacterDatabase.Query("SELECT name FROM reserved_name");
8540
8541 if (!result)
8542 {
8543 TC_LOG_INFO("server.loading", ">> Loaded 0 reserved player names. DB table `reserved_name` is empty!");
8544 return;
8545 }
8546
8547 uint32 count = 0;
8548
8549 Field* fields;
8550 do
8551 {
8552 fields = result->Fetch();
8553 std::string name= fields[0].GetString();
8554
8555 std::wstring wstr;
8556 if (!Utf8toWStr (name, wstr))
8557 {
8558 TC_LOG_ERROR("misc", "Table `reserved_name` has invalid name: {}", name);
8559 continue;
8560 }
8561
8562 wstrToLower(wstr);
8563
8564 _reservedNamesStore.insert(wstr);
8565 ++count;
8566 }
8567 while (result->NextRow());
8568
8569 TC_LOG_INFO("server.loading", ">> Loaded {} reserved player names in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8570}
8571
8572bool ObjectMgr::IsReservedName(std::string_view name) const
8573{
8574 std::wstring wstr;
8575 if (!Utf8toWStr (name, wstr))
8576 return false;
8577
8578 wstrToLower(wstr);
8579
8580 return _reservedNamesStore.find(wstr) != _reservedNamesStore.end();
8581}
8582
8584{
8587 LT_CYRILLIC = 0x0002,
8589 LT_ANY = 0xFFFF
8591
8593{
8594 switch (sWorld->getIntConfig(CONFIG_REALM_ZONE))
8595 {
8596 case REALM_ZONE_UNKNOWN: // any language
8600 return LT_ANY;
8601 case REALM_ZONE_UNITED_STATES: // extended-Latin
8602 case REALM_ZONE_OCEANIC:
8604 case REALM_ZONE_ENGLISH:
8605 case REALM_ZONE_GERMAN:
8606 case REALM_ZONE_FRENCH:
8607 case REALM_ZONE_SPANISH:
8608 return LT_EXTENDEN_LATIN;
8609 case REALM_ZONE_KOREA: // East-Asian
8610 case REALM_ZONE_TAIWAN:
8611 case REALM_ZONE_CHINA:
8612 return LT_EAST_ASIA;
8613 case REALM_ZONE_RUSSIAN: // Cyrillic
8614 return LT_CYRILLIC;
8615 default:
8616 return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
8617 }
8618}
8619
8620bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
8621{
8622 if (strictMask == 0) // any language, ignore realm
8623 {
8624 if (isExtendedLatinString(wstr, numericOrSpace))
8625 return true;
8626 if (isCyrillicString(wstr, numericOrSpace))
8627 return true;
8628 if (isEastAsianString(wstr, numericOrSpace))
8629 return true;
8630 return false;
8631 }
8632
8633 if (strictMask & 0x2) // realm zone specific
8634 {
8636 if (lt & LT_EXTENDEN_LATIN)
8637 if (isExtendedLatinString(wstr, numericOrSpace))
8638 return true;
8639 if (lt & LT_CYRILLIC)
8640 if (isCyrillicString(wstr, numericOrSpace))
8641 return true;
8642 if (lt & LT_EAST_ASIA)
8643 if (isEastAsianString(wstr, numericOrSpace))
8644 return true;
8645 }
8646
8647 if (strictMask & 0x1) // basic Latin
8648 {
8649 if (isBasicLatinString(wstr, numericOrSpace))
8650 return true;
8651 }
8652
8653 return false;
8654}
8655
8656ResponseCodes ObjectMgr::CheckPlayerName(std::string_view name, LocaleConstant locale, bool create /*= false*/)
8657{
8658 std::wstring wname;
8659 if (!Utf8toWStr(name, wname))
8661
8662 if (wname.size() > MAX_PLAYER_NAME)
8663 return CHAR_NAME_TOO_LONG;
8664
8665 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
8666 if (wname.size() < minName)
8667 return CHAR_NAME_TOO_SHORT;
8668
8669 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
8670 if (!isValidString(wname, strictMask, false, create))
8672
8673 wstrToLower(wname);
8674 for (size_t i = 2; i < wname.size(); ++i)
8675 if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
8677
8678 return ValidateName(wname, locale);
8679}
8680
8681bool ObjectMgr::IsValidCharterName(std::string_view name)
8682{
8683 std::wstring wname;
8684 if (!Utf8toWStr(name, wname))
8685 return false;
8686
8687 if (wname.size() > MAX_CHARTER_NAME)
8688 return false;
8689
8690 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
8691 if (wname.size() < minName)
8692 return false;
8693
8694 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);
8695
8696 return isValidString(wname, strictMask, true);
8697}
8698
8700{
8701 std::wstring wname;
8702 if (!Utf8toWStr(name, wname))
8703 return PET_NAME_INVALID;
8704
8705 if (wname.size() > MAX_PET_NAME)
8706 return PET_NAME_TOO_LONG;
8707
8708 uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
8709 if (wname.size() < minName)
8710 return PET_NAME_TOO_SHORT;
8711
8712 uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
8713 if (!isValidString(wname, strictMask, false))
8715
8716 switch (ValidateName(wname, locale))
8717 {
8718 case CHAR_NAME_PROFANE:
8719 return PET_NAME_PROFANE;
8720 case CHAR_NAME_RESERVED:
8721 return PET_NAME_RESERVED;
8722 case RESPONSE_FAILURE:
8723 return PET_NAME_INVALID;
8724 default:
8725 break;
8726 }
8727 return PET_NAME_SUCCESS;
8728}
8729
8731{
8732 uint32 oldMSTime = getMSTime();
8733
8734 _gameObjectForQuestStore.clear(); // need for reload case
8735
8736 if (_gameObjectTemplateStore.empty())
8737 {
8738 TC_LOG_INFO("server.loading", ">> Loaded 0 GameObjects for quests");
8739 return;
8740 }
8741
8742 uint32 count = 0;
8743
8744 // collect GO entries for GO that must activated
8745 for (auto const& gameObjectTemplatePair : _gameObjectTemplateStore)
8746 {
8747 switch (gameObjectTemplatePair.second.type)
8748 {
8750 break;
8752 {
8753 // scan GO chest with loot including quest items
8754 uint32 lootId = gameObjectTemplatePair.second.GetLootId();
8755 // find quest loot for GO
8756 if (gameObjectTemplatePair.second.chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(lootId))
8757 break;
8758 continue;
8759 }
8761 {
8762 if (gameObjectTemplatePair.second._generic.questID > 0) //quests objects
8763 break;
8764 continue;
8765 }
8767 {
8768 if (gameObjectTemplatePair.second.goober.questId > 0) //quests objects
8769 break;
8770 continue;
8771 }
8772 default:
8773 continue;
8774 }
8775
8776 _gameObjectForQuestStore.insert(gameObjectTemplatePair.first);
8777 ++count;
8778 }
8779
8780 TC_LOG_INFO("server.loading", ">> Loaded {} GameObjects for quests in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8781}
8782
8784{
8785 uint32 oldMSTime = getMSTime();
8786
8787 _trinityStringStore.clear(); // for reload case
8788
8789 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");
8790 if (!result)
8791 {
8792 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.");
8793 return false;
8794 }
8795
8796 do
8797 {
8798 Field* fields = result->Fetch();
8799
8800 uint32 entry = fields[0].GetUInt32();
8801
8802 TrinityString& data = _trinityStringStore[entry];
8803
8804 data.Content.resize(DEFAULT_LOCALE + 1);
8805
8806 for (int8 i = TOTAL_LOCALES - 1; i >= 0; --i)
8807 AddLocaleString(fields[i + 1].GetString(), LocaleConstant(i), data.Content);
8808 }
8809 while (result->NextRow());
8810
8811 TC_LOG_INFO("server.loading", ">> Loaded {} trinity strings in {} ms", _trinityStringStore.size(), GetMSTimeDiffToNow(oldMSTime));
8812 return true;
8813}
8814
8815char const* ObjectMgr::GetTrinityString(uint32 entry, LocaleConstant locale) const
8816{
8817 if (TrinityString const* ts = GetTrinityString(entry))
8818 {
8819 if (ts->Content.size() > size_t(locale) && !ts->Content[locale].empty())
8820 return ts->Content[locale].c_str();
8821 return ts->Content[DEFAULT_LOCALE].c_str();
8822 }
8823
8824 TC_LOG_ERROR("sql.sql", "Trinity string entry {} not found in DB.", entry);
8825 return "<error>";
8826}
8827
8829{
8830 uint32 oldMSTime = getMSTime();
8831
8832 _fishingBaseForAreaStore.clear(); // for reload case
8833
8834 QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");
8835
8836 if (!result)
8837 {
8838 TC_LOG_INFO("server.loading", ">> Loaded 0 areas for fishing base skill level. DB table `skill_fishing_base_level` is empty.");
8839 return;
8840 }
8841
8842 uint32 count = 0;
8843
8844 do
8845 {
8846 Field* fields = result->Fetch();
8847 uint32 entry = fields[0].GetUInt32();
8848 int32 skill = fields[1].GetInt16();
8849
8850 AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry);
8851 if (!fArea)
8852 {
8853 TC_LOG_ERROR("sql.sql", "AreaId {} defined in `skill_fishing_base_level` does not exist", entry);
8854 continue;
8855 }
8856
8857 _fishingBaseForAreaStore[entry] = skill;
8858 ++count;
8859 }
8860 while (result->NextRow());
8861
8862 TC_LOG_INFO("server.loading", ">> Loaded {} areas for fishing base skill level in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8863}
8864
8865bool ObjectMgr::CheckDeclinedNames(const std::wstring& w_ownname, DeclinedName const& names)
8866{
8867 // get main part of the name
8868 std::wstring mainpart = GetMainPartOfName(w_ownname, 0);
8869 // prepare flags
8870 bool x = true;
8871 bool y = true;
8872
8873 // check declined names
8874 for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
8875 {
8876 std::wstring wname;
8877 if (!Utf8toWStr(names.name[i], wname))
8878 return false;
8879
8880 if (mainpart != GetMainPartOfName(wname, i+1))
8881 x = false;
8882
8883 if (w_ownname != wname)
8884 y = false;
8885 }
8886 return (x || y);
8887}
8888
8890{
8891 AreaTriggerScriptContainer::const_iterator i = _areaTriggerScriptStore.find(trigger_id);
8892 if (i!= _areaTriggerScriptStore.end())
8893 return i->second;
8894 return 0;
8895}
8896
8898{
8899 return _spellScriptsStore.equal_range(spellId);
8900}
8901
8902// this allows calculating base reputations to offline players, just by race and class
8903int32 ObjectMgr::GetBaseReputationOf(FactionEntry const* factionEntry, uint8 race, uint8 playerClass) const
8904{
8905 if (!factionEntry)
8906 return 0;
8907
8908 uint32 raceMask = (1 << (race - 1));
8909 uint32 classMask = (1 << (playerClass-1));
8910
8911 for (uint8 i = 0; i < 4; ++i)
8912 {
8913 if ((!factionEntry->ReputationClassMask[i] ||
8914 factionEntry->ReputationClassMask[i] & classMask) &&
8915 (!factionEntry->ReputationRaceMask[i] ||
8916 factionEntry->ReputationRaceMask[i] & raceMask))
8917 return factionEntry->ReputationBase[i];
8918 }
8919
8920 return 0;
8921}
8922
8924{
8925 SkillLineEntry const* skill = sSkillLineStore.LookupEntry(rcEntry->SkillID);
8926 if (!skill)
8927 return SKILL_RANGE_NONE;
8928
8929 if (sSkillTiersStore.LookupEntry(rcEntry->SkillTierID))
8930 return SKILL_RANGE_RANK;
8931
8932 if (rcEntry->SkillID == SKILL_RUNEFORGING)
8933 return SKILL_RANGE_MONO;
8934
8935 switch (skill->CategoryID)
8936 {
8938 return SKILL_RANGE_MONO;
8940 return SKILL_RANGE_LANGUAGE;
8941 }
8942
8943 return SKILL_RANGE_LEVEL;
8944}
8945
8947{
8948 uint32 oldMSTime = getMSTime();
8949
8950 _gameTeleStore.clear(); // for reload case
8951
8952 // 0 1 2 3 4 5 6
8953 QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
8954
8955 if (!result)
8956 {
8957 TC_LOG_INFO("server.loading", ">> Loaded 0 GameTeleports. DB table `game_tele` is empty!");
8958 return;
8959 }
8960
8961 uint32 count = 0;
8962
8963 do
8964 {
8965 Field* fields = result->Fetch();
8966
8967 uint32 id = fields[0].GetUInt32();
8968
8969 GameTele gt;
8970
8971 gt.position_x = fields[1].GetFloat();
8972 gt.position_y = fields[2].GetFloat();
8973 gt.position_z = fields[3].GetFloat();
8974 gt.orientation = fields[4].GetFloat();
8975 gt.mapId = fields[5].GetUInt16();
8976 gt.name = fields[6].GetString();
8977
8979 {
8980 TC_LOG_ERROR("sql.sql", "Wrong position for id {} (name: {}) in `game_tele` table, ignoring.", id, gt.name);
8981 continue;
8982 }
8983
8984 if (!Utf8toWStr(gt.name, gt.wnameLow))
8985 {
8986 TC_LOG_ERROR("sql.sql", "Wrong UTF8 name for id {} in `game_tele` table, ignoring.", id);
8987 continue;
8988 }
8989
8991
8992 _gameTeleStore[id] = gt;
8993
8994 ++count;
8995 }
8996 while (result->NextRow());
8997
8998 TC_LOG_INFO("server.loading", ">> Loaded {} GameTeleports in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
8999}
9000
9001GameTele const* ObjectMgr::GetGameTele(std::string_view name) const
9002{
9003 // explicit name case
9004 std::wstring wname;
9005 if (!Utf8toWStr(name, wname))
9006 return nullptr;
9007
9008 // converting string that we try to find to lower case
9009 wstrToLower(wname);
9010
9011 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
9012 GameTele const* alt = nullptr;
9013 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9014 {
9015 if (itr->second.wnameLow == wname)
9016 return &itr->second;
9017 else if (!alt && itr->second.wnameLow.find(wname) != std::wstring::npos)
9018 alt = &itr->second;
9019 }
9020
9021 return alt;
9022}
9023
9024GameTele const* ObjectMgr::GetGameTeleExactName(std::string_view name) const
9025{
9026 // explicit name case
9027 std::wstring wname;
9028 if (!Utf8toWStr(name, wname))
9029 return nullptr;
9030
9031 // converting string that we try to find to lower case
9032 wstrToLower(wname);
9033
9034 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9035 {
9036 if (itr->second.wnameLow == wname)
9037 return &itr->second;
9038 }
9039
9040 return nullptr;
9041}
9042
9044{
9045 // find max id
9046 uint32 new_id = 0;
9047 for (GameTeleContainer::const_iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9048 if (itr->first > new_id)
9049 new_id = itr->first;
9050
9051 // use next
9052 ++new_id;
9053
9054 if (!Utf8toWStr(tele.name, tele.wnameLow))
9055 return false;
9056
9057 wstrToLower(tele.wnameLow);
9058
9059 _gameTeleStore[new_id] = tele;
9060
9062
9063 stmt->setUInt32(0, new_id);
9064 stmt->setFloat(1, tele.position_x);
9065 stmt->setFloat(2, tele.position_y);
9066 stmt->setFloat(3, tele.position_z);
9067 stmt->setFloat(4, tele.orientation);
9068 stmt->setUInt16(5, uint16(tele.mapId));
9069 stmt->setString(6, tele.name);
9070
9071 WorldDatabase.Execute(stmt);
9072
9073 return true;
9074}
9075
9076bool ObjectMgr::DeleteGameTele(std::string_view name)
9077{
9078 // explicit name case
9079 std::wstring wname;
9080 if (!Utf8toWStr(name, wname))
9081 return false;
9082
9083 // converting string that we try to find to lower case
9084 wstrToLower(wname);
9085
9086 for (GameTeleContainer::iterator itr = _gameTeleStore.begin(); itr != _gameTeleStore.end(); ++itr)
9087 {
9088 if (itr->second.wnameLow == wname)
9089 {
9091
9092 stmt->setString(0, itr->second.name);
9093
9094 WorldDatabase.Execute(stmt);
9095
9096 _gameTeleStore.erase(itr);
9097 return true;
9098 }
9099 }
9100
9101 return false;
9102}
9103
9105{
9106 uint32 oldMSTime = getMSTime();
9107
9108 _mailLevelRewardStore.clear(); // for reload case
9109
9110 // 0 1 2 3
9111 QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
9112
9113 if (!result)
9114 {
9115 TC_LOG_INFO("server.loading", ">> Loaded 0 level dependent mail rewards. DB table `mail_level_reward` is empty.");
9116 return;
9117 }
9118
9119 uint32 count = 0;
9120
9121 do
9122 {
9123 Field* fields = result->Fetch();
9124
9125 uint8 level = fields[0].GetUInt8();
9126 uint32 raceMask = fields[1].GetUInt32();
9127 uint32 mailTemplateId = fields[2].GetUInt32();
9128 uint32 senderEntry = fields[3].GetUInt32();
9129
9130 if (level > MAX_LEVEL)
9131 {
9132 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has data for level {} that more supported by client ({}), ignoring.", level, MAX_LEVEL);
9133 continue;
9134 }
9135
9136 if (!(raceMask & RACEMASK_ALL_PLAYABLE))
9137 {
9138 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has raceMask ({}) for level {} that not include any player races, ignoring.", raceMask, level);
9139 continue;
9140 }
9141
9142 if (!sMailTemplateStore.LookupEntry(mailTemplateId))
9143 {
9144 TC_LOG_ERROR("sql.sql", "Table `mail_level_reward` has invalid mailTemplateId ({}) for level {} that invalid not include any player races, ignoring.", mailTemplateId, level);
9145 continue;
9146 }
9147
9148 if (!GetCreatureTemplate(senderEntry))
9149 {
9150 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);
9151 continue;
9152 }
9153
9154 _mailLevelRewardStore[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));
9155
9156 ++count;
9157 }
9158 while (result->NextRow());
9159
9160 TC_LOG_INFO("server.loading", ">> Loaded {} level dependent mail rewards in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9161}
9162
9164{
9165 uint32 oldMSTime = getMSTime();
9166
9167 // For reload case
9168 _trainers.clear();
9169
9170 std::unordered_map<int32, std::vector<Trainer::Spell>> spellsByTrainer;
9171 if (QueryResult trainerSpellsResult = WorldDatabase.Query("SELECT TrainerId, SpellId, MoneyCost, ReqSkillLine, ReqSkillRank, ReqAbility1, ReqAbility2, ReqAbility3, ReqLevel FROM trainer_spell"))
9172 {
9173 do
9174 {
9175 Field* fields = trainerSpellsResult->Fetch();
9176
9177 Trainer::Spell spell;
9178 uint32 trainerId = fields[0].GetUInt32();
9179 spell.SpellId = fields[1].GetUInt32();
9180 spell.MoneyCost = fields[2].GetUInt32();
9181 spell.ReqSkillLine = fields[3].GetUInt32();
9182 spell.ReqSkillRank = fields[4].GetUInt32();
9183 spell.ReqAbility[0] = fields[5].GetUInt32();
9184 spell.ReqAbility[1] = fields[6].GetUInt32();
9185 spell.ReqAbility[2] = fields[7].GetUInt32();
9186 spell.ReqLevel = fields[8].GetUInt8();
9187
9188 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell.SpellId);
9189 if (!spellInfo)
9190 {
9191 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) for TrainerId {}, ignoring", spell.SpellId, trainerId);
9192 continue;
9193 }
9194
9195 if (GetTalentSpellCost(spell.SpellId))
9196 {
9197 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (SpellId: {}) which is a talent, for TrainerId {}, ignoring", spell.SpellId, trainerId);
9198 continue;
9199 }
9200
9201 if (spell.ReqSkillLine && !sSkillLineStore.LookupEntry(spell.ReqSkillLine))
9202 {
9203 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing skill (ReqSkillLine: {}) for TrainerId {} and SpellId {}, ignoring",
9204 spell.ReqSkillLine, trainerId, spell.SpellId);
9205 continue;
9206 }
9207
9208 bool allReqValid = true;
9209 for (std::size_t i = 0; i < spell.ReqAbility.size(); ++i)
9210 {
9211 uint32 requiredSpell = spell.ReqAbility[i];
9212 if (requiredSpell && !sSpellMgr->GetSpellInfo(requiredSpell))
9213 {
9214 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing spell (ReqAbility{}: {}) for TrainerId {} and SpellId {}, ignoring",
9215 i + 1, requiredSpell, trainerId, spell.SpellId);
9216 allReqValid = false;
9217 }
9218 }
9219
9220 if (!allReqValid)
9221 continue;
9222
9223 spellsByTrainer[trainerId].push_back(spell);
9224 } while (trainerSpellsResult->NextRow());
9225 }
9226
9227 if (QueryResult trainersResult = WorldDatabase.Query("SELECT Id, Type, Requirement, Greeting FROM trainer"))
9228 {
9229 do
9230 {
9231 Field* fields = trainersResult->Fetch();
9232
9233 uint32 trainerId = fields[0].GetUInt32();
9234 Trainer::Type trainerType = Trainer::Type(fields[1].GetUInt8());
9235 uint32 requirement = fields[2].GetUInt32();
9236 std::string greeting = fields[3].GetString();
9237 std::vector<Trainer::Spell> spells;
9238 auto spellsItr = spellsByTrainer.find(trainerId);
9239 if (spellsItr != spellsByTrainer.end())
9240 {
9241 spells = std::move(spellsItr->second);
9242 spellsByTrainer.erase(spellsItr);
9243 }
9244
9245 switch (trainerType)
9246 {
9248 case Trainer::Type::Pet:
9249 if (requirement && !sChrClassesStore.LookupEntry(requirement))
9250 {
9251 TC_LOG_ERROR("sql.sql", "Table `trainer` references non-existing class requirement {} for TrainerId {}, ignoring", requirement, trainerId);
9252 continue;
9253 }
9254 break;
9256 if (requirement && !sChrRacesStore.LookupEntry(requirement))
9257 {
9258 TC_LOG_ERROR("sql.sql", "Table `trainer` references non-existing race requirement {} for TrainerId {}, ignoring", requirement, trainerId);
9259 continue;
9260 }
9261 break;
9263 if (requirement && !sSpellMgr->GetSpellInfo(requirement))
9264 {
9265 TC_LOG_ERROR("sql.sql", "Table `trainer` references non-existing spell requirement {} for TrainerId {}, ignoring", requirement, trainerId);
9266 continue;
9267 }
9268 break;
9269 default:
9270 break;
9271 }
9272
9273 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)));
9274 ASSERT(isNew);
9275 if (trainerType == Trainer::Type::Class)
9276 _classTrainers[requirement].push_back(&it->second);
9277 } while (trainersResult->NextRow());
9278 }
9279
9280 for (auto const& unusedSpells : spellsByTrainer)
9281 {
9282 for (Trainer::Spell const& unusedSpell : unusedSpells.second)
9283 {
9284 TC_LOG_ERROR("sql.sql", "Table `trainer_spell` references non-existing trainer (TrainerId: {}) for SpellId {}, ignoring", unusedSpells.first, unusedSpell.SpellId);
9285 }
9286 }
9287
9288 if (QueryResult trainerLocalesResult = WorldDatabase.Query("SELECT Id, locale, Greeting_lang FROM trainer_locale"))
9289 {
9290 do
9291 {
9292 Field* fields = trainerLocalesResult->Fetch();
9293 uint32 trainerId = fields[0].GetUInt32();
9294 std::string localeName = fields[1].GetString();
9295
9296 LocaleConstant locale = GetLocaleByName(localeName);
9297 if (locale == LOCALE_enUS)
9298 continue;
9299
9300 if (Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId))
9301 trainer->AddGreetingLocale(locale, fields[2].GetString());
9302 else
9303 TC_LOG_ERROR("sql.sql", "Table `trainer_locale` references non-existing trainer (TrainerId: {}) for locale {}, ignoring",
9304 trainerId, localeName);
9305 } while (trainerLocalesResult->NextRow());
9306 }
9307
9308 TC_LOG_INFO("server.loading", ">> Loaded {} Trainers in {} ms", _trainers.size(), GetMSTimeDiffToNow(oldMSTime));
9309}
9310
9312{
9313 uint32 oldMSTime = getMSTime();
9314
9315 _creatureDefaultTrainers.clear();
9316
9317 if (QueryResult result = WorldDatabase.Query("SELECT CreatureId, TrainerId FROM creature_default_trainer"))
9318 {
9319 do
9320 {
9321 Field* fields = result->Fetch();
9322 uint32 creatureId = fields[0].GetUInt32();
9323 uint32 trainerId = fields[1].GetUInt32();
9324
9325 if (!GetCreatureTemplate(creatureId))
9326 {
9327 TC_LOG_ERROR("sql.sql", "Table `creature_default_trainer` references non-existing creature template (CreatureId: {}), ignoring", creatureId);
9328 continue;
9329 }
9330
9331 Trainer::Trainer* trainer = Trinity::Containers::MapGetValuePtr(_trainers, trainerId);
9332 if (!trainer)
9333 {
9334 TC_LOG_ERROR("sql.sql", "Table `creature_default_trainer` references non-existing trainer (TrainerId: {}) for CreatureId {}, ignoring", trainerId, creatureId);
9335 continue;
9336 }
9337
9338 _creatureDefaultTrainers[creatureId] = trainer;
9339
9340 } while (result->NextRow());
9341 }
9342
9343 TC_LOG_INFO("server.loading", ">> Loaded {} default trainers in {} ms", _creatureDefaultTrainers.size(), GetMSTimeDiffToNow(oldMSTime));
9344}
9345
9346uint32 ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
9347{
9348 // find all items from the reference vendor
9350 stmt->setUInt32(0, uint32(item));
9351 PreparedQueryResult result = WorldDatabase.Query(stmt);
9352
9353 if (!result)
9354 return 0;
9355
9356 uint32 count = 0;
9357 do
9358 {
9359 Field* fields = result->Fetch();
9360
9361 int32 item_id = fields[0].GetInt32();
9362
9363 // if item is a negative, its a reference
9364 if (item_id < 0)
9365 count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
9366 else
9367 {
9368 int32 maxcount = fields[1].GetUInt8();
9369 uint32 incrtime = fields[2].GetUInt32();
9370 uint32 ExtendedCost = fields[3].GetUInt32();
9371
9372 if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, nullptr, skip_vendors))
9373 continue;
9374
9375 VendorItemData& vList = _cacheVendorItemStore[vendor];
9376
9377 vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
9378 ++count;
9379 }
9380 } while (result->NextRow());
9381
9382 return count;
9383}
9384
9386{
9387 uint32 oldMSTime = getMSTime();
9388
9389 // For reload case
9390 for (CacheVendorItemContainer::iterator itr = _cacheVendorItemStore.begin(); itr != _cacheVendorItemStore.end(); ++itr)
9391 itr->second.Clear();
9392 _cacheVendorItemStore.clear();
9393
9394 std::set<uint32> skip_vendors;
9395
9396 QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor ORDER BY entry, slot ASC");
9397 if (!result)
9398 {
9399
9400 TC_LOG_ERROR("sql.sql", ">> Loaded 0 Vendors. DB table `npc_vendor` is empty!");
9401 return;
9402 }
9403
9404 uint32 count = 0;
9405
9406 do
9407 {
9408 Field* fields = result->Fetch();
9409
9410 uint32 entry = fields[0].GetUInt32();
9411 int32 item_id = fields[1].GetInt32();
9412
9413 // if item is a negative, its a reference
9414 if (item_id < 0)
9415 count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
9416 else
9417 {
9418 uint32 maxcount = fields[2].GetUInt8();
9419 uint32 incrtime = fields[3].GetUInt32();
9420 uint32 ExtendedCost = fields[4].GetUInt32();
9421
9422 if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, nullptr, &skip_vendors))
9423 continue;
9424
9425 VendorItemData& vList = _cacheVendorItemStore[entry];
9426
9427 vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
9428 ++count;
9429 }
9430 }
9431 while (result->NextRow());
9432
9433 TC_LOG_INFO("server.loading", ">> Loaded {} Vendors in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9434}
9435
9437{
9438 uint32 oldMSTime = getMSTime();
9439
9440 _gossipMenusStore.clear();
9441
9442 // 0 1
9443 QueryResult result = WorldDatabase.Query("SELECT MenuID, TextID FROM gossip_menu");
9444
9445 if (!result)
9446 {
9447 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu IDs. DB table `gossip_menu` is empty!");
9448 return;
9449 }
9450
9451 do
9452 {
9453 Field* fields = result->Fetch();
9454
9455 GossipMenus gMenu;
9456
9457 gMenu.MenuID = fields[0].GetUInt16();
9458 gMenu.TextID = fields[1].GetUInt32();
9459
9460 if (!GetGossipText(gMenu.TextID))
9461 {
9462 TC_LOG_ERROR("sql.sql", "Table gossip_menu: ID {} is using non-existing TextID {}", gMenu.MenuID, gMenu.TextID);
9463 continue;
9464 }
9465
9466 _gossipMenusStore.insert(GossipMenusContainer::value_type(gMenu.MenuID, gMenu));
9467 } while (result->NextRow());
9468
9469 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu IDs in {} ms", uint32(_gossipMenusStore.size()), GetMSTimeDiffToNow(oldMSTime));
9470}
9471
9473{
9474 uint32 oldMSTime = getMSTime();
9475
9476 _gossipMenuItemsStore.clear();
9477
9478 QueryResult result = WorldDatabase.Query(
9479 // 0 1 2 3 4 5 6 7 8 9 10 11 12
9480 "SELECT MenuID, OptionID, OptionIcon, OptionText, OptionBroadcastTextID, OptionType, OptionNpcFlag, ActionMenuID, ActionPoiID, BoxCoded, BoxMoney, BoxText, BoxBroadcastTextID "
9481 "FROM gossip_menu_option ORDER BY MenuID, OptionID");
9482
9483 if (!result)
9484 {
9485 TC_LOG_INFO("server.loading", ">> Loaded 0 gossip_menu_option IDs. DB table `gossip_menu_option` is empty!");
9486 return;
9487 }
9488
9489 do
9490 {
9491 Field* fields = result->Fetch();
9492
9493 GossipMenuItems gMenuItem;
9494
9495 gMenuItem.MenuID = fields[0].GetUInt16();
9496 gMenuItem.OptionID = fields[1].GetUInt16();
9497 gMenuItem.OptionIcon = GossipOptionIcon(fields[2].GetUInt32());
9498 gMenuItem.OptionText = fields[3].GetString();
9499 gMenuItem.OptionBroadcastTextID = fields[4].GetUInt32();
9500 gMenuItem.OptionType = fields[5].GetUInt8();
9501 gMenuItem.OptionNpcFlag = fields[6].GetUInt32();
9502 gMenuItem.ActionMenuID = fields[7].GetUInt32();
9503 gMenuItem.ActionPoiID = fields[8].GetUInt32();
9504 gMenuItem.BoxCoded = fields[9].GetBool();
9505 gMenuItem.BoxMoney = fields[10].GetUInt32();
9506 gMenuItem.BoxText = fields[11].GetString();
9507 gMenuItem.BoxBroadcastTextID = fields[12].GetUInt32();
9508
9509 if (gMenuItem.OptionIcon >= GOSSIP_ICON_MAX)
9510 {
9511 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);
9512 gMenuItem.OptionIcon = GOSSIP_ICON_CHAT;
9513 }
9514
9515 if (gMenuItem.OptionBroadcastTextID)
9516 {
9517 if (!GetBroadcastText(gMenuItem.OptionBroadcastTextID))
9518 {
9519 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);
9520 gMenuItem.OptionBroadcastTextID = 0;
9521 }
9522 }
9523
9524 if (gMenuItem.OptionType >= GOSSIP_OPTION_MAX)
9525 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);
9526
9527 if (gMenuItem.ActionPoiID && !GetPointOfInterest(gMenuItem.ActionPoiID))
9528 {
9529 TC_LOG_ERROR("sql.sql", "Table `gossip_menu_option` for menu {}, id {} use non-existing ActionPoiID {}, ignoring", gMenuItem.MenuID, gMenuItem.OptionID, gMenuItem.ActionPoiID);
9530 gMenuItem.ActionPoiID = 0;
9531 }
9532
9533 if (gMenuItem.BoxBroadcastTextID)
9534 {
9535 if (!GetBroadcastText(gMenuItem.BoxBroadcastTextID))
9536 {
9537 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);
9538 gMenuItem.BoxBroadcastTextID = 0;
9539 }
9540 }
9541
9542 _gossipMenuItemsStore.insert(GossipMenuItemsContainer::value_type(gMenuItem.MenuID, gMenuItem));
9543 } while (result->NextRow());
9544
9545 TC_LOG_INFO("server.loading", ">> Loaded {} gossip_menu_option entries in {} ms", uint32(_gossipMenuItemsStore.size()), GetMSTimeDiffToNow(oldMSTime));
9546}
9547
9549{
9550 auto itr = _creatureDefaultTrainers.find(creatureId);
9551 if (itr != _creatureDefaultTrainers.end())
9552 return itr->second;
9553
9554 return nullptr;
9555}
9556
9557void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedCost, bool persist /*= true*/)
9558{
9559 VendorItemData& vList = _cacheVendorItemStore[entry];
9560 vList.AddItem(item, maxcount, incrtime, extendedCost);
9561
9562 if (persist)
9563 {
9565
9566 stmt->setUInt32(0, entry);
9567 stmt->setUInt32(1, item);
9568 stmt->setUInt8(2, maxcount);
9569 stmt->setUInt32(3, incrtime);
9570 stmt->setUInt32(4, extendedCost);
9571
9572 WorldDatabase.Execute(stmt);
9573 }
9574}
9575
9576bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= true*/)
9577{
9578 CacheVendorItemContainer::iterator iter = _cacheVendorItemStore.find(entry);
9579 if (iter == _cacheVendorItemStore.end())
9580 return false;
9581
9582 if (!iter->second.RemoveItem(item))
9583 return false;
9584
9585 if (persist)
9586 {
9588
9589 stmt->setUInt32(0, entry);
9590 stmt->setUInt32(1, item);
9591
9592 WorldDatabase.Execute(stmt);
9593 }
9594
9595 return true;
9596}
9597
9598bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, uint32 id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* player, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
9599{
9600 CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(vendor_entry);
9601 if (!cInfo)
9602 {
9603 if (player)
9605 else
9606 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for nonexistent creature template (Entry: {}), ignore", vendor_entry);
9607 return false;
9608 }
9609
9610 if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
9611 {
9612 if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
9613 {
9614 if (player)
9616 else
9617 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has data for creature template (Entry: {}) without vendor flag, ignore", vendor_entry);
9618
9619 if (skip_vendors)
9620 skip_vendors->insert(vendor_entry);
9621 }
9622 return false;
9623 }
9624
9625 if (!sObjectMgr->GetItemTemplate(id))
9626 {
9627 if (player)
9629 else
9630 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` for Vendor (Entry: {}) have in item list non-existed item ({}), ignore", vendor_entry, id);
9631 return false;
9632 }
9633
9634 if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
9635 {
9636 if (player)
9638 else
9639 TC_LOG_ERROR("sql.sql", "Table `(game_event_)npc_vendor` has Item (Entry: {}) with wrong ExtendedCost ({}) for vendor ({}), ignore", id, ExtendedCost, vendor_entry);
9640 return false;
9641 }
9642
9643 if (maxcount > 0 && incrtime == 0)
9644 {
9645 if (player)
9646 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
9647 else
9648 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);
9649 return false;
9650 }
9651 else if (maxcount == 0 && incrtime > 0)
9652 {
9653 if (player)
9654 ChatHandler(player->GetSession()).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
9655 else
9656 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);
9657 return false;
9658 }
9659
9660 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
9661 if (!vItems)
9662 return true; // later checks for non-empty lists
9663
9664 if (vItems->FindItemCostPair(id, ExtendedCost))
9665 {
9666 if (player)
9668 else
9669 TC_LOG_ERROR("sql.sql", "Table `npc_vendor` has duplicate items {} (with extended cost {}) for vendor (Entry: {}), ignoring", id, ExtendedCost, vendor_entry);
9670 return false;
9671 }
9672
9673 return true;
9674}
9675
9677{
9678 uint32 oldMSTime = getMSTime();
9679
9680 // We insert an empty placeholder here so we can use the
9681 // script id 0 as dummy for "no script found".
9682 _scriptNamesStore.emplace_back("");
9683
9684 QueryResult result = WorldDatabase.Query(
9685 "SELECT DISTINCT(ScriptName) FROM achievement_criteria_data WHERE ScriptName <> '' AND type = 11 "
9686 "UNION "
9687 "SELECT DISTINCT(ScriptName) FROM battlefield_template WHERE ScriptName <> '' "
9688 "UNION "
9689 "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' "
9690 "UNION "
9691 "SELECT DISTINCT(ScriptName) FROM creature WHERE ScriptName <> '' "
9692 "UNION "
9693 "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
9694 "UNION "
9695 "SELECT DISTINCT(ScriptName) FROM gameobject WHERE ScriptName <> '' "
9696 "UNION "
9697 "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
9698 "UNION "
9699 "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' "
9700 "UNION "
9701 "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
9702 "UNION "
9703 "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
9704 "UNION "
9705 "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
9706 "UNION "
9707 "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' "
9708 "UNION "
9709 "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' "
9710 "UNION "
9711 "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' "
9712 "UNION "
9713 "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
9714
9715 if (!result)
9716 {
9717 TC_LOG_INFO("server.loading", ">> Loaded empty set of Script Names!");
9718 return;
9719 }
9720
9721 _scriptNamesStore.reserve(result->GetRowCount() + 1);
9722
9723 do
9724 {
9725 _scriptNamesStore.push_back((*result)[0].GetString());
9726 }
9727 while (result->NextRow());
9728
9729 std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
9730
9731 TC_LOG_INFO("server.loading", ">> Loaded {} ScriptNames in {} ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
9732}
9733
9735{
9736 return _scriptNamesStore;
9737}
9738
9739std::string const& ObjectMgr::GetScriptName(uint32 id) const
9740{
9741 static std::string const empty = "";
9742 return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
9743}
9744
9745uint32 ObjectMgr::GetScriptId(std::string const& name)
9746{
9747 // use binary search to find the script name in the sorted vector
9748 // assume "" is the first element
9749 if (name.empty())
9750 return 0;
9751
9752 ScriptNameContainer::const_iterator itr = std::lower_bound(_scriptNamesStore.begin(), _scriptNamesStore.end(), name);
9753 if (itr == _scriptNamesStore.end() || (*itr != name))
9754 return 0;
9755
9756 return uint32(itr - _scriptNamesStore.begin());
9757}
9758
9760{
9761 uint32 oldMSTime = getMSTime();
9762
9763 _broadcastTextStore.clear(); // for reload case
9764
9765 // 0 1 2 3 4 5 6 7 8 9 10 11 12
9766 QueryResult result = WorldDatabase.Query("SELECT ID, LanguageID, `Text`, Text1, EmoteID1, EmoteID2, EmoteID3, EmoteDelay1, EmoteDelay2, EmoteDelay3, SoundEntriesID, EmotesID, Flags FROM broadcast_text");
9767 if (!result)
9768 {
9769 TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast texts. DB table `broadcast_text` is empty.");
9770 return;
9771 }
9772
9773 _broadcastTextStore.rehash(result->GetRowCount());
9774
9775 do
9776 {
9777 Field* fields = result->Fetch();
9778
9779 BroadcastText bct;
9780
9781 bct.Id = fields[0].GetUInt32();
9782 bct.LanguageID = fields[1].GetUInt32();
9783 bct.Text[DEFAULT_LOCALE] = fields[2].GetString();
9784 bct.Text1[DEFAULT_LOCALE] = fields[3].GetString();
9785 bct.EmoteId1 = fields[4].GetUInt32();
9786 bct.EmoteId2 = fields[5].GetUInt32();
9787 bct.EmoteId3 = fields[6].GetUInt32();
9788 bct.EmoteDelay1 = fields[7].GetUInt32();
9789 bct.EmoteDelay2 = fields[8].GetUInt32();
9790 bct.EmoteDelay3 = fields[9].GetUInt32();
9791 bct.SoundEntriesID = fields[10].GetUInt32();
9792 bct.EmotesID = fields[11].GetUInt32();
9793 bct.Flags = fields[12].GetUInt32();
9794
9795 if (bct.SoundEntriesID)
9796 {
9797 if (!sSoundEntriesStore.LookupEntry(bct.SoundEntriesID))
9798 {
9799 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` has SoundEntriesID {} but sound does not exist.", bct.Id, bct.SoundEntriesID);
9800 bct.SoundEntriesID = 0;
9801 }
9802 }
9803
9805 {
9806 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` using LanguageID {} but Language does not exist.", bct.Id, bct.LanguageID);
9808 }
9809
9810 if (bct.EmoteId1)
9811 {
9812 if (!sEmotesStore.LookupEntry(bct.EmoteId1))
9813 {
9814 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` has EmoteId1 {} but emote does not exist.", bct.Id, bct.EmoteId1);
9815 bct.EmoteId1 = 0;
9816 }
9817 }
9818
9819 if (bct.EmoteId2)
9820 {
9821 if (!sEmotesStore.LookupEntry(bct.EmoteId2))
9822 {
9823 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` has EmoteId2 {} but emote does not exist.", bct.Id, bct.EmoteId2);
9824 bct.EmoteId2 = 0;
9825 }
9826 }
9827
9828 if (bct.EmoteId3)
9829 {
9830 if (!sEmotesStore.LookupEntry(bct.EmoteId3))
9831 {
9832 TC_LOG_DEBUG("broadcasttext", "BroadcastText (Id: {}) in table `broadcast_text` has EmoteId3 {} but emote does not exist.", bct.Id, bct.EmoteId3);
9833 bct.EmoteId3 = 0;
9834 }
9835 }
9836
9837 _broadcastTextStore[bct.Id] = bct;
9838 }
9839 while (result->NextRow());
9840
9841 TC_LOG_INFO("server.loading", ">> Loaded {} broadcast texts in {} ms", _broadcastTextStore.size(), GetMSTimeDiffToNow(oldMSTime));
9842}
9843
9845{
9846 uint32 oldMSTime = getMSTime();
9847
9848 // 0 1 2 3
9849 QueryResult result = WorldDatabase.Query("SELECT ID, locale, `Text`, Text1 FROM broadcast_text_locale");
9850 if (!result)
9851 {
9852 TC_LOG_INFO("server.loading", ">> Loaded 0 broadcast text locales. DB table `broadcast_text_locale` is empty.");
9853 return;
9854 }
9855
9856 do
9857 {
9858 Field* fields = result->Fetch();
9859
9860 uint32 id = fields[0].GetUInt32();
9861 std::string localeName = fields[1].GetString();
9862
9863 LocaleConstant locale = GetLocaleByName(localeName);
9864 if (locale == LOCALE_enUS)
9865 continue;
9866
9867 BroadcastTextContainer::iterator bct = _broadcastTextStore.find(id);
9868 if (bct == _broadcastTextStore.end())
9869 {
9870 TC_LOG_ERROR("sql.sql", "BroadcastText (Id: {}) in table `broadcast_text_locale` does not exist. Skipped!", id);
9871 continue;
9872 }
9873
9874 AddLocaleString(fields[2].GetString(), locale, bct->second.Text);
9875 AddLocaleString(fields[3].GetString(), locale, bct->second.Text1);
9876 } while (result->NextRow());
9877
9878 TC_LOG_INFO("server.loading", ">> Loaded {} broadcast text locales in {} ms", uint32(_broadcastTextStore.size()), GetMSTimeDiffToNow(oldMSTime));
9879}
9880
9882{
9883 CreatureBaseStatsContainer::const_iterator it = _creatureBaseStatsStore.find(MAKE_PAIR16(level, unitClass));
9884
9885 if (it != _creatureBaseStatsStore.end())
9886 return &(it->second);
9887
9888 struct DefaultCreatureBaseStats : public CreatureBaseStats
9889 {
9890 DefaultCreatureBaseStats()
9891 {
9892 BaseArmor = 1;
9893 for (uint8 j = 0; j < MAX_EXPANSIONS; ++j)
9894 {
9895 BaseHealth[j] = 1;
9896 BaseDamage[j] = 0.0f;
9897 }
9898 BaseMana = 0;
9899 AttackPower = 0;
9900 RangedAttackPower = 0;
9901 }
9902 };
9903 static const DefaultCreatureBaseStats defStats;
9904 return &defStats;
9905}
9906
9908{
9909 uint32 oldMSTime = getMSTime();
9910
9911 QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2 FROM creature_classlevelstats");
9912
9913 if (!result)
9914 {
9915 TC_LOG_INFO("server.loading", ">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
9916 return;
9917 }
9918
9919 uint32 count = 0;
9920 do
9921 {
9922 Field* fields = result->Fetch();
9923
9924 uint8 Level = fields[0].GetUInt8();
9925 uint8 Class = fields[1].GetUInt8();
9926
9927 if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
9928 TC_LOG_ERROR("sql.sql", "Creature base stats for level {} has invalid class {}", Level, Class);
9929
9930 CreatureBaseStats stats;
9931
9932 for (uint8 i = 0; i < MAX_EXPANSIONS; ++i)
9933 {
9934 stats.BaseHealth[i] = fields[2 + i].GetUInt32();
9935
9936 if (stats.BaseHealth[i] == 0)
9937 {
9938 TC_LOG_ERROR("sql.sql", "Creature base stats for class {}, level {} has invalid zero base HP[{}] - set to 1", Class, Level, i);
9939 stats.BaseHealth[i] = 1;
9940 }
9941
9942 stats.BaseDamage[i] = fields[9 + i].GetFloat();
9943 if (stats.BaseDamage[i] < 0.0f)
9944 {
9945 TC_LOG_ERROR("sql.sql", "Creature base stats for class {}, level {} has invalid negative base damage[{}] - set to 0.0", Class, Level, i);
9946 stats.BaseDamage[i] = 0.0f;
9947 }
9948 }
9949
9950 stats.BaseMana = fields[5].GetUInt32();
9951 stats.BaseArmor = fields[6].GetUInt32();
9952
9953 stats.AttackPower = fields[7].GetUInt16();
9954 stats.RangedAttackPower = fields[8].GetUInt16();
9955
9956 _creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
9957
9958 ++count;
9959 }
9960 while (result->NextRow());
9961
9962 for (auto const& creatureTemplatePair : _creatureTemplateStore)
9963 {
9964 for (uint16 lvl = creatureTemplatePair.second.minlevel; lvl <= creatureTemplatePair.second.maxlevel; ++lvl)
9965 {
9966 if (!_creatureBaseStatsStore.count(MAKE_PAIR16(lvl, creatureTemplatePair.second.unit_class)))
9967 TC_LOG_ERROR("sql.sql", "Missing base stats for creature class {} level {}", creatureTemplatePair.second.unit_class, lvl);
9968 }
9969 }
9970
9971 TC_LOG_INFO("server.loading", ">> Loaded {} creature base stats in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
9972}
9973
9975{
9976 uint32 oldMSTime = getMSTime();
9977
9978 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");
9979
9980 if (!result)
9981 {
9982 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
9983 return;
9984 }
9985
9986 uint32 count = 0;
9987
9988 do
9989 {
9990 Field* fields = result->Fetch();
9991
9992 uint32 alliance = fields[0].GetUInt32();
9993 uint32 horde = fields[1].GetUInt32();
9994
9995 if (!sAchievementStore.LookupEntry(alliance))
9996 TC_LOG_ERROR("sql.sql", "Achievement {} (alliance_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
9997 else if (!sAchievementStore.LookupEntry(horde))
9998 TC_LOG_ERROR("sql.sql", "Achievement {} (horde_id) referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
9999 else
10000 FactionChangeAchievements[alliance] = horde;
10001
10002 ++count;
10003 }
10004 while (result->NextRow());
10005
10006 TC_LOG_INFO("server.loading", ">> Loaded {} faction change achievement pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10007}
10008
10010{
10011 uint32 oldMSTime = getMSTime();
10012
10013 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");
10014
10015 if (!result)
10016 {
10017 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty.");
10018 return;
10019 }
10020
10021 uint32 count = 0;
10022
10023 do
10024 {
10025 Field* fields = result->Fetch();
10026
10027 uint32 alliance = fields[0].GetUInt32();
10028 uint32 horde = fields[1].GetUInt32();
10029
10030 if (!GetItemTemplate(alliance))
10031 TC_LOG_ERROR("sql.sql", "Item {} (alliance_id) referenced in `player_factionchange_items` does not exist, pair skipped!", alliance);
10032 else if (!GetItemTemplate(horde))
10033 TC_LOG_ERROR("sql.sql", "Item {} (horde_id) referenced in `player_factionchange_items` does not exist, pair skipped!", horde);
10034 else
10035 FactionChangeItems[alliance] = horde;
10036
10037 ++count;
10038 }
10039 while (result->NextRow());
10040
10041 TC_LOG_INFO("server.loading", ">> Loaded {} faction change item pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10042}
10043
10045{
10046 uint32 oldMSTime = getMSTime();
10047
10048 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_quests");
10049
10050 if (!result)
10051 {
10052 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change quest pairs. DB table `player_factionchange_quests` is empty.");
10053 return;
10054 }
10055
10056 uint32 count = 0;
10057
10058 do
10059 {
10060 Field* fields = result->Fetch();
10061
10062 uint32 alliance = fields[0].GetUInt32();
10063 uint32 horde = fields[1].GetUInt32();
10064
10065 if (!sObjectMgr->GetQuestTemplate(alliance))
10066 TC_LOG_ERROR("sql.sql", "Quest {} (alliance_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", alliance);
10067 else if (!sObjectMgr->GetQuestTemplate(horde))
10068 TC_LOG_ERROR("sql.sql", "Quest {} (horde_id) referenced in `player_factionchange_quests` does not exist, pair skipped!", horde);
10069 else
10070 FactionChangeQuests[alliance] = horde;
10071
10072 ++count;
10073 }
10074 while (result->NextRow());
10075
10076 TC_LOG_INFO("server.loading", ">> Loaded {} faction change quest pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10077}
10078
10080{
10081 uint32 oldMSTime = getMSTime();
10082
10083 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");
10084
10085 if (!result)
10086 {
10087 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
10088 return;
10089 }
10090
10091 uint32 count = 0;
10092
10093 do
10094 {
10095 Field* fields = result->Fetch();
10096
10097 uint32 alliance = fields[0].GetUInt32();
10098 uint32 horde = fields[1].GetUInt32();
10099
10100 if (!sFactionStore.LookupEntry(alliance))
10101 TC_LOG_ERROR("sql.sql", "Reputation {} (alliance_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
10102 else if (!sFactionStore.LookupEntry(horde))
10103 TC_LOG_ERROR("sql.sql", "Reputation {} (horde_id) referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
10104 else
10105 FactionChangeReputation[alliance] = horde;
10106
10107 ++count;
10108 }
10109 while (result->NextRow());
10110
10111 TC_LOG_INFO("server.loading", ">> Loaded {} faction change reputation pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10112}
10113
10115{
10116 uint32 oldMSTime = getMSTime();
10117
10118 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");
10119
10120 if (!result)
10121 {
10122 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
10123 return;
10124 }
10125
10126 uint32 count = 0;
10127
10128 do
10129 {
10130 Field* fields = result->Fetch();
10131
10132 uint32 alliance = fields[0].GetUInt32();
10133 uint32 horde = fields[1].GetUInt32();
10134
10135 if (!sSpellMgr->GetSpellInfo(alliance))
10136 TC_LOG_ERROR("sql.sql", "Spell {} (alliance_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
10137 else if (!sSpellMgr->GetSpellInfo(horde))
10138 TC_LOG_ERROR("sql.sql", "Spell {} (horde_id) referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
10139 else
10140 FactionChangeSpells[alliance] = horde;
10141
10142 ++count;
10143 }
10144 while (result->NextRow());
10145
10146 TC_LOG_INFO("server.loading", ">> Loaded {} faction change spell pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10147}
10148
10150{
10151 uint32 oldMSTime = getMSTime();
10152
10153 QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_titles");
10154
10155 if (!result)
10156 {
10157 TC_LOG_INFO("server.loading", ">> Loaded 0 faction change title pairs. DB table `player_factionchange_title` is empty.");
10158 return;
10159 }
10160
10161 uint32 count = 0;
10162
10163 do
10164 {
10165 Field* fields = result->Fetch();
10166
10167 uint32 alliance = fields[0].GetUInt32();
10168 uint32 horde = fields[1].GetUInt32();
10169
10170 if (!sCharTitlesStore.LookupEntry(alliance))
10171 TC_LOG_ERROR("sql.sql", "Title {} (alliance_id) referenced in `player_factionchange_title` does not exist, pair skipped!", alliance);
10172 else if (!sCharTitlesStore.LookupEntry(horde))
10173 TC_LOG_ERROR("sql.sql", "Title {} (horde_id) referenced in `player_factionchange_title` does not exist, pair skipped!", horde);
10174 else
10175 FactionChangeTitles[alliance] = horde;
10176
10177 ++count;
10178 }
10179 while (result->NextRow());
10180
10181 TC_LOG_INFO("server.loading", ">> Loaded {} faction change title pairs in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10182}
10183
10185{
10186 return Trinity::Containers::MapGetValuePtr(_gameObjectTemplateStore, entry);
10187}
10188
10190{
10191 auto itr = _gameObjectTemplateAddonStore.find(entry);
10192 if (itr != _gameObjectTemplateAddonStore.end())
10193 return &itr->second;
10194
10195 return nullptr;
10196}
10197
10199{
10200 return Trinity::Containers::MapGetValuePtr(_gameObjectOverrideStore, spawnId);
10201}
10202
10204{
10205 return Trinity::Containers::MapGetValuePtr(_creatureTemplateStore, entry);
10206}
10207
10209{
10210 return Trinity::Containers::MapGetValuePtr(_questPOIStore, questId);
10211}
10212
10214{
10215 return Trinity::Containers::MapGetValuePtr(_vehicleTemplateStore, veh->GetCreatureEntry());
10216}
10217
10219{
10220 if (Creature* cre = veh->GetBase()->ToCreature())
10221 {
10222 // Give preference to GUID-based accessories
10223 VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(cre->GetSpawnId());
10224 if (itr != _vehicleAccessoryStore.end())
10225 return &itr->second;
10226 }
10227
10228 // Otherwise return entry-based
10229 VehicleAccessoryTemplateContainer::const_iterator itr = _vehicleTemplateAccessoryStore.find(veh->GetCreatureEntry());
10230 if (itr != _vehicleTemplateAccessoryStore.end())
10231 return &itr->second;
10232 return nullptr;
10233}
10234
10236{
10237 auto itr = _dungeonEncounterStore.find(MAKE_PAIR32(mapId, difficulty));
10238 if (itr != _dungeonEncounterStore.end())
10239 return &itr->second;
10240 return nullptr;
10241}
10242
10244{
10245 if (race >= MAX_RACES)
10246 return nullptr;
10247 if (class_ >= MAX_CLASSES)
10248 return nullptr;
10249 auto const& info = _playerInfo[race][class_];
10250 if (!info)
10251 return nullptr;
10252 return info.get();
10253}
10254
10256{
10257 uint32 oldMSTime = getMSTime();
10258
10259 // 0 1 2
10260 QueryResult result = WorldDatabase.Query("SELECT GameObjectEntry, ItemId, Idx FROM gameobject_questitem ORDER BY Idx ASC");
10261
10262 if (!result)
10263 {
10264 TC_LOG_INFO("server.loading", ">> Loaded 0 gameobject quest items. DB table `gameobject_questitem` is empty.");
10265 return;
10266 }
10267
10268 uint32 count = 0;
10269 do
10270 {
10271 Field* fields = result->Fetch();
10272
10273 uint32 entry = fields[0].GetUInt32();
10274 uint32 item = fields[1].GetUInt32();
10275 uint32 idx = fields[2].GetUInt32();
10276
10277 GameObjectTemplate const* goInfo = GetGameObjectTemplate(entry);
10278 if (!goInfo)
10279 {
10280 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has data for nonexistent gameobject (entry: {}, idx: {}), skipped", entry, idx);
10281 continue;
10282 };
10283
10284 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10285 if (!db2Data)
10286 {
10287 TC_LOG_ERROR("sql.sql", "Table `gameobject_questitem` has nonexistent item (ID: {}) in gameobject (entry: {}, idx: {}), skipped", item, entry, idx);
10288 continue;
10289 };
10290
10291 _gameObjectQuestItemStore[entry].push_back(item);
10292
10293 ++count;
10294 }
10295 while (result->NextRow());
10296
10297 TC_LOG_INFO("server.loading", ">> Loaded {} gameobject quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10298}
10299
10301{
10302 uint32 oldMSTime = getMSTime();
10303
10304 // 0 1 2
10305 QueryResult result = WorldDatabase.Query("SELECT CreatureEntry, ItemId, Idx FROM creature_questitem ORDER BY Idx ASC");
10306
10307 if (!result)
10308 {
10309 TC_LOG_INFO("server.loading", ">> Loaded 0 creature quest items. DB table `creature_questitem` is empty.");
10310 return;
10311 }
10312
10313 uint32 count = 0;
10314 do
10315 {
10316 Field* fields = result->Fetch();
10317
10318 uint32 entry = fields[0].GetUInt32();
10319 uint32 item = fields[1].GetUInt32();
10320 uint32 idx = fields[2].GetUInt32();
10321
10322 CreatureTemplate const* creatureInfo = GetCreatureTemplate(entry);
10323 if (!creatureInfo)
10324 {
10325 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has data for nonexistent creature (entry: {}, idx: {}), skipped", entry, idx);
10326 continue;
10327 };
10328
10329 ItemEntry const* db2Data = sItemStore.LookupEntry(item);
10330 if (!db2Data)
10331 {
10332 TC_LOG_ERROR("sql.sql", "Table `creature_questitem` has nonexistent item (ID: {}) in creature (entry: {}, idx: {}), skipped", item, entry, idx);
10333 continue;
10334 };
10335
10336 _creatureQuestItemStore[entry].push_back(item);
10337
10338 ++count;
10339 }
10340 while (result->NextRow());
10341
10342 TC_LOG_INFO("server.loading", ">> Loaded {} creature quest items in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
10343}
10344
10346{
10347 uint32 oldMSTime = getMSTime();
10348
10349 // cache disabled
10350 if (!sWorld->getBoolConfig(CONFIG_CACHE_DATA_QUERIES))
10351 {
10352 TC_LOG_INFO("server.loading", ">> Query data caching is disabled. Skipped initialization.");
10353 return;
10354 }
10355
10357
10358 // Initialize Query data for creatures
10359 if (mask & QUERY_DATA_CREATURES)
10360 for (auto& creatureTemplatePair : _creatureTemplateStore)
10361 pool.PostWork([creature = &creatureTemplatePair.second]() { creature->InitializeQueryData(); });
10362
10363 // Initialize Query Data for gameobjects
10364 if (mask & QUERY_DATA_GAMEOBJECTS)
10365 for (auto& gameObjectTemplatePair : _gameObjectTemplateStore)
10366 pool.PostWork([gobj = &gameObjectTemplatePair.second]() { gobj->InitializeQueryData(); });
10367
10368 // Initialize Query Data for items
10369 if (mask & QUERY_DATA_ITEMS)
10370 for (auto& itemTemplatePair : _itemTemplateStore)
10371 pool.PostWork([item = &itemTemplatePair.second]() { item->InitializeQueryData(); });
10372
10373 // Initialize Query Data for quests
10374 if (mask & QUERY_DATA_QUESTS)
10375 for (auto& questTemplatePair : _questTemplates)
10376 pool.PostWork([quest = questTemplatePair.second.get()]() { quest->InitializeQueryData(); });
10377
10378 // Initialize Quest POI data
10379 if (mask & QUERY_DATA_POIS)
10380 for (auto& poiWrapperPair : _questPOIStore)
10381 pool.PostWork([poi = &poiWrapperPair.second]() { poi->InitializeQueryData(); });
10382
10383 pool.Join();
10384
10385 TC_LOG_INFO("server.loading", ">> Initialized query cache data in {} ms", GetMSTimeDiffToNow(oldMSTime));
10386}
10387
10389{
10390 QueryDataBuffer = BuildQueryData();
10391}
10392
10394{
10395 ByteBuffer tempBuffer;
10396 tempBuffer << uint32(POIData.QuestID); // quest ID
10397 tempBuffer << uint32(POIData.QuestPOIBlobDataStats.size()); // POI count
10398
10399 for (QuestPOIBlobData const& questPOIBlobData : POIData.QuestPOIBlobDataStats)
10400 {
10401 tempBuffer << uint32(questPOIBlobData.BlobIndex); // POI index
10402 tempBuffer << int32(questPOIBlobData.ObjectiveIndex); // objective index
10403 tempBuffer << uint32(questPOIBlobData.MapID); // mapid
10404 tempBuffer << uint32(questPOIBlobData.WorldMapAreaID); // areaid
10405 tempBuffer << uint32(questPOIBlobData.Floor); // floorid
10406 tempBuffer << uint32(questPOIBlobData.Unk3); // unknown
10407 tempBuffer << uint32(questPOIBlobData.Unk4); // unknown
10408 tempBuffer << uint32(questPOIBlobData.QuestPOIBlobPointStats.size()); // POI points count
10409
10410 for (QuestPOIBlobPoint const& questPOIBlobPoint : questPOIBlobData.QuestPOIBlobPointStats)
10411 {
10412 tempBuffer << int32(questPOIBlobPoint.X); // POI point x
10413 tempBuffer << int32(questPOIBlobPoint.Y); // POI point y
10414 }
10415 }
10416
10417 return tempBuffer;
10418}
#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:72
#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:296
@ 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)
MapDifficulty const * GetMapDifficultyData(uint32 mapId, Difficulty difficulty)
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)
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:59
@ GOSSIP_ICON_CHAT
Definition GossipDef.h:60
@ GOSSIP_ICON_MAX
Definition GossipDef.h:81
@ GOSSIP_OPTION_MAX
Definition GossipDef.h:55
#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 was copied. Do not allow making a copy of items in mail.
Definition Mail.h:50
@ MAIL_CHECK_MASK_RETURNED
Definition Mail.h:48
std::vector< MailItemInfo > MailItemInfoVec
Definition Mail.h:164
@ MAIL_NORMAL
Definition Mail.h:37
#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:861
#define MAX_PLAYER_NAME
Definition ObjectMgr.h:876
std::pair< GraveyardContainer::const_iterator, GraveyardContainer::const_iterator > GraveyardMapBounds
Definition ObjectMgr.h:860
std::multimap< uint32, ScriptInfo > ScriptMap
Definition ObjectMgr.h:410
#define MAX_CHARTER_NAME
Definition ObjectMgr.h:879
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:917
@ QUERY_DATA_CREATURES
Definition ObjectMgr.h:918
@ QUERY_DATA_QUESTS
Definition ObjectMgr.h:921
@ QUERY_DATA_GAMEOBJECTS
Definition ObjectMgr.h:919
@ QUERY_DATA_ITEMS
Definition ObjectMgr.h:920
@ QUERY_DATA_POIS
Definition ObjectMgr.h:922
LanguageDesc const * GetLanguageDescByID(uint32 lang)
std::unordered_map< uint32, CellObjectGuids > CellObjectGuidsMap
Definition ObjectMgr.h:521
#define SPAWNGROUP_MAP_UNSET
Definition ObjectMgr.h:882
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:895
@ ENCOUNTER_CREDIT_KILL_CREATURE
Definition ObjectMgr.h:896
@ ENCOUNTER_CREDIT_CAST_SPELL
Definition ObjectMgr.h:897
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:584
std::vector< std::unique_ptr< DungeonEncounter const > > DungeonEncounterList
Definition ObjectMgr.h:911
#define MAX_PET_NAME
Definition ObjectMgr.h:878
SkillRangeType
Definition ObjectMgr.h:866
@ SKILL_RANGE_MONO
Definition ObjectMgr.h:869
@ SKILL_RANGE_NONE
Definition ObjectMgr.h:871
@ SKILL_RANGE_LANGUAGE
Definition ObjectMgr.h:867
@ SKILL_RANGE_RANK
Definition ObjectMgr.h:870
@ SKILL_RANGE_LEVEL
Definition ObjectMgr.h:868
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition Optional.h:25
#define QUEST_REWARDS_COUNT
Definition QuestDef.h:42
#define QUEST_ITEM_OBJECTIVES_COUNT
Definition QuestDef.h:39
#define QUEST_OBJECTIVES_COUNT
Definition QuestDef.h:38
@ QUEST_FLAGS_TRACKING
Definition QuestDef.h:143
@ QUEST_FLAGS_DAILY
Definition QuestDef.h:145
@ QUEST_FLAGS_WEEKLY
Definition QuestDef.h:148
#define QUEST_REPUTATIONS_COUNT
Definition QuestDef.h:44
#define QUEST_SOURCE_ITEM_IDS_COUNT
Definition QuestDef.h:40
#define QUEST_REWARD_CHOICES_COUNT
Definition QuestDef.h:41
@ QUEST_SPECIAL_FLAGS_COMPLETED_AT_START
Definition QuestDef.h:176
@ QUEST_SPECIAL_FLAGS_CAST
Definition QuestDef.h:166
@ QUEST_SPECIAL_FLAGS_TIMED
Definition QuestDef.h:174
@ QUEST_SPECIAL_FLAGS_DELIVER
Definition QuestDef.h:171
@ QUEST_SPECIAL_FLAGS_REPEATABLE
Definition QuestDef.h:161
@ QUEST_SPECIAL_FLAGS_KILL
Definition QuestDef.h:173
@ QUEST_SPECIAL_FLAGS_DB_ALLOWED
Definition QuestDef.h:169
@ QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT
Definition QuestDef.h:162
@ QUEST_SPECIAL_FLAGS_SPEAKTO
Definition QuestDef.h:172
@ QUEST_SPECIAL_FLAGS_MONTHLY
Definition QuestDef.h:165
@ QUEST_SPECIAL_FLAGS_PLAYER_KILL
Definition QuestDef.h:175
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
@ 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:69
virtual void SendSysMessage(std::string_view str, bool escapeCharacters=false)
Definition Chat.cpp:101
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
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:281
bool IsRemovalGrid(float x, float y) const
Definition Map.h:323
bool Instanceable() const
Definition Map.cpp:4226
bool IsGridLoaded(uint32 gridId) const
Definition Map.h:330
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:1434
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:961
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:1266
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:1522
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:1359
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:1313
void ReturnOrDeleteOldMails(bool serverUp)
void LoadGameObjectAddons()
SpawnGroupTemplateData const * GetDefaultSpawnGroup() const
Definition ObjectMgr.h:1265
GameObjectData & NewOrExistGameObjectData(ObjectGuid::LowType spawnId)
Definition ObjectMgr.h:1365
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:1465
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:1338
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:1344
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:186
ObjectGuid GetGUID() const
Definition Object.h:79
static Player * ToPlayer(Object *o)
Definition Object.h:180
WorldSession * GetSession() const
Definition Player.h:1719
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)
bool HasSpecialFlag(uint32 flag) const
Definition QuestDef.h:222
uint32 ItemDropQuantity[QUEST_SOURCE_ITEM_IDS_COUNT]
Definition QuestDef.h:307
int32 _prevQuestId
Definition QuestDef.h:396
uint32 _requiredSkillPoints
Definition QuestDef.h:403
int32 RequiredNpcOrGo[QUEST_OBJECTIVES_COUNT]
Definition QuestDef.h:308
std::vector< uint32 > DependentBreadcrumbQuests
Definition QuestDef.h:339
int32 _breadcrumbForQuestId
Definition QuestDef.h:399
uint32 _requiredClasses
Definition QuestDef.h:394
uint32 RequiredNpcOrGoCount[QUEST_OBJECTIVES_COUNT]
Definition QuestDef.h:309
void LoadQuestDetails(Field *fields)
Definition QuestDef.cpp:123
uint32 _requiredFactionId2
Definition QuestDef.h:360
void SetSpecialFlag(uint32 flag)
Definition QuestDef.h:223
static bool IsTakingQuestEnabled(uint32 questId)
Definition QuestDef.cpp:230
uint32 GetCharTitleId() const
Definition QuestDef.h:255
int32 _rewardSpell
Definition QuestDef.h:384
uint32 RewardItemIdCount[QUEST_REWARDS_COUNT]
Definition QuestDef.h:313
void LoadQuestRequestItems(Field *fields)
Definition QuestDef.cpp:140
uint32 _requiredMinRepFaction
Definition QuestDef.h:404
uint32 _requiredSkillId
Definition QuestDef.h:402
uint32 _startItemCount
Definition QuestDef.h:408
int32 _requiredMinRepValue
Definition QuestDef.h:405
uint32 _flags
Definition QuestDef.h:364
uint32 RewardChoiceItemId[QUEST_REWARD_CHOICES_COUNT]
Definition QuestDef.h:310
uint32 ItemDrop[QUEST_SOURCE_ITEM_IDS_COUNT]
Definition QuestDef.h:306
uint32 _timeAllowed
Definition QuestDef.h:363
uint32 _nextQuestId
Definition QuestDef.h:397
uint32 GetSrcItemCount() const
Definition QuestDef.h:261
void LoadQuestMailSender(Field *fields)
Definition QuestDef.cpp:197
int32 RewardFactionValueIdOverride[QUEST_REPUTATIONS_COUNT]
Definition QuestDef.h:316
int32 _requiredFactionValue2
Definition QuestDef.h:361
int32 _exclusiveGroup
Definition QuestDef.h:398
uint32 _requiredFactionId1
Definition QuestDef.h:358
uint32 RewardChoiceItemCount[QUEST_REWARD_CHOICES_COUNT]
Definition QuestDef.h:311
int32 _requiredMaxRepValue
Definition QuestDef.h:407
uint32 _requiredMaxRepFaction
Definition QuestDef.h:406
std::vector< uint32 > DependentPreviousQuests
Definition QuestDef.h:338
uint32 _allowableRaces
Definition QuestDef.h:357
uint32 GetQuestId() const
Definition QuestDef.h:229
uint32 _rewardNextQuest
Definition QuestDef.h:369
void LoadQuestOfferReward(Field *fields)
Definition QuestDef.cpp:154
uint32 _rewardMailDelay
Definition QuestDef.h:401
uint32 _requiredPlayerKills
Definition QuestDef.h:366
uint32 RequiredItemCount[QUEST_ITEM_OBJECTIVES_COUNT]
Definition QuestDef.h:305
uint32 _sourceSpellid
Definition QuestDef.h:395
int32 GetBreadcrumbForQuestId() const
Definition QuestDef.h:253
uint32 _rewardDisplaySpell
Definition QuestDef.h:383
uint32 RequiredItemId[QUEST_ITEM_OBJECTIVES_COUNT]
Definition QuestDef.h:304
uint32 _specialFlags
Definition QuestDef.h:410
uint32 _startItem
Definition QuestDef.h:371
uint32 GetSrcItemId() const
Definition QuestDef.h:260
uint32 RewardFactionId[QUEST_REPUTATIONS_COUNT]
Definition QuestDef.h:314
int32 _requiredFactionValue1
Definition QuestDef.h:359
int32 RewardFactionValueId[QUEST_REPUTATIONS_COUNT]
Definition QuestDef.h:315
uint32 _id
Definition QuestDef.h:351
uint32 _rewardMailSenderEntry
Definition QuestDef.h:409
int32 _zoneOrSort
Definition QuestDef.h:353
uint32 _rewardTitleId
Definition QuestDef.h:365
uint32 RewardItemId[QUEST_REWARDS_COUNT]
Definition QuestDef.h:312
uint32 GetQuestMethod() const
Definition QuestDef.h:230
uint32 _rewardMailTemplateId
Definition QuestDef.h:400
void LoadQuestTemplateAddon(Field *fields)
Definition QuestDef.cpp:173
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:439
Unit * GetSummonerUnit() const
decltype(auto) PostWork(T &&work)
Definition ThreadPool.h:33
Definition Unit.h:769
TempSummon * ToTempSummon()
Definition Unit.h:1794
bool IsSummon() const
Definition Unit.h:882
bool IsInRaidWith(Unit const *unit) const
Definition Unit.cpp:11892
bool IsInPartyWith(Unit const *unit) const
Definition Unit.cpp:11873
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:382
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2801
static void StopNow(uint8 exitcode)
Definition World.h:673
#define sWorld
Definition World.h:900
@ REALM_ZONE_TEST_SERVER
Definition World.h:520
@ REALM_ZONE_LATIN_AMERICA
Definition World.h:498
@ REALM_ZONE_SPANISH
Definition World.h:505
@ REALM_ZONE_OCEANIC
Definition World.h:497
@ REALM_ZONE_KOREA
Definition World.h:500
@ REALM_ZONE_UNKNOWN
Definition World.h:494
@ REALM_ZONE_UNITED_STATES
Definition World.h:496
@ REALM_ZONE_ENGLISH
Definition World.h:502
@ REALM_ZONE_FRENCH
Definition World.h:504
@ REALM_ZONE_QA_SERVER
Definition World.h:522
@ REALM_ZONE_CHINA
Definition World.h:510
@ REALM_ZONE_RUSSIAN
Definition World.h:506
@ REALM_ZONE_DEVELOPMENT
Definition World.h:495
@ REALM_ZONE_TAIWAN
Definition World.h:508
@ REALM_ZONE_GERMAN
Definition World.h:503
@ CONFIG_STRICT_PET_NAMES
Definition World.h:224
@ CONFIG_MIN_PET_NAME
Definition World.h:227
@ CONFIG_MAX_PLAYER_LEVEL
Definition World.h:236
@ CONFIG_MIN_CHARTER_NAME
Definition World.h:226
@ CONFIG_STRICT_CHARTER_NAMES
Definition World.h:223
@ CONFIG_REALM_ZONE
Definition World.h:221
@ CONFIG_STRICT_PLAYER_NAMES
Definition World.h:222
@ CONFIG_EXPANSION
Definition World.h:282
@ CONFIG_MIN_PLAYER_NAME
Definition World.h:225
@ 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)
uint32 EmoteDelay2
Definition ObjectMgr.h:489
std::vector< std::string > Text1
Definition ObjectMgr.h:484
uint32 EmotesID
Definition ObjectMgr.h:492
uint32 LanguageID
Definition ObjectMgr.h:482
std::vector< std::string > Text
Definition ObjectMgr.h:483
uint32 EmoteId2
Definition ObjectMgr.h:486
uint32 EmoteDelay1
Definition ObjectMgr.h:488
uint32 SoundEntriesID
Definition ObjectMgr.h:491
uint32 EmoteId3
Definition ObjectMgr.h:487
uint32 EmoteId1
Definition ObjectMgr.h:485
uint32 EmoteDelay3
Definition ObjectMgr.h:490
CellGuidSet gameobjects
Definition ObjectMgr.h:519
CellGuidSet creatures
Definition ObjectMgr.h:518
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 modelid_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:131
std::string Title
uint32 DifficultyEntry[MAX_DIFFICULTY - 1]
std::string AIName
uint32 GetRandomValidModelId() const
Definition Creature.cpp:109
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]
int32 ReputationIndex
uint32 ParentFactionID
uint32 ReputationRaceMask[4]
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:569
std::vector< std::string > OptionText
Definition ObjectMgr.h:568
uint32 BoxBroadcastTextID
Definition ObjectMgr.h:785
std::string OptionText
Definition ObjectMgr.h:776
uint32 ActionMenuID
Definition ObjectMgr.h:780
uint32 OptionNpcFlag
Definition ObjectMgr.h:779
uint32 ActionPoiID
Definition ObjectMgr.h:781
std::string BoxText
Definition ObjectMgr.h:784
GossipOptionIcon OptionIcon
Definition ObjectMgr.h:775
uint32 OptionType
Definition ObjectMgr.h:778
uint32 OptionBroadcastTextID
Definition ObjectMgr.h:777
uint32 TextID
Definition ObjectMgr.h:792
uint32 MenuID
Definition ObjectMgr.h:791
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:855
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< 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:708
uint32 armor
Definition ObjectMgr.h:710
uint16 minDamage
Definition ObjectMgr.h:711
uint16 stats[MAX_STATS]
Definition ObjectMgr.h:707
uint16 maxDamage
Definition ObjectMgr.h:712
uint16 mana
Definition ObjectMgr.h:709
uint8 stats[MAX_STATS]
Definition ObjectMgr.h:657
std::vector< std::string > Name
Definition ObjectMgr.h:576
std::string Name
Definition ObjectMgr.h:768
uint32 Importance
Definition ObjectMgr.h:767
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:531
std::vector< std::string > AreaDescription
Definition QuestDef.h:188
std::vector< std::string > CompletedText
Definition QuestDef.h:189
std::vector< std::string > Title
Definition QuestDef.h:183
std::vector< std::string > Objectives
Definition QuestDef.h:185
std::vector< std::vector< std::string > > ObjectiveText
Definition QuestDef.h:190
std::vector< std::string > Details
Definition QuestDef.h:184
std::vector< std::string > RewardText
Definition QuestDef.h:200
std::vector< QuestPOIBlobPoint > QuestPOIBlobPointStats
Definition ObjectMgr.h:818
uint32 WorldMapAreaID
Definition ObjectMgr.h:814
std::vector< QuestPOIBlobData > QuestPOIBlobDataStats
Definition ObjectMgr.h:824
uint32 QuestID
Definition ObjectMgr.h:823
QuestPOIData POIData
Definition ObjectMgr.h:829
ByteBuffer BuildQueryData() const
void InitializeQueryData()
QuestRelations::const_iterator _it
Definition ObjectMgr.h:615
QuestRelations::const_iterator _end
Definition ObjectMgr.h:615
bool HasQuest(uint32 questId) const
QuestRelations::const_iterator _end
Definition ObjectMgr.h:629
QuestRelations::const_iterator _begin
Definition ObjectMgr.h:629
std::vector< std::string > CompletionText
Definition QuestDef.h:195
float questMonthlyRate
Definition ObjectMgr.h:734
float questWeeklyRate
Definition ObjectMgr.h:733
float questDailyRate
Definition ObjectMgr.h:732
float questRepeatableRate
Definition ObjectMgr.h:735
float spellRate
Definition ObjectMgr.h:737
float questRate
Definition ObjectMgr.h:731
float creatureRate
Definition ObjectMgr.h:736
uint32 faction_rank[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:757
uint32 faction[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:755
float faction_rate[MAX_SPILLOVER_FACTIONS]
Definition ObjectMgr.h:756
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:526
Milliseconds DespawnDelay
void AddItem(uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost)
VendorItem const * FindItemCostPair(uint32 item_id, uint32 extendedCost) const
Definition Creature.cpp:101
DBCPosition3D Loc