2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "ObjectMgr.h"
20 #include "Database/DatabaseEnv.h"
21 #include "Policies/SingletonImp.h"
23 #include "SQLStorages.h"
25 #include "MapManager.h"
26 #include "ObjectGuid.h"
27 #include "ScriptMgr.h"
29 #include "UpdateMask.h"
32 #include "ArenaTeam.h"
33 #include "Transports.h"
34 #include "ProgressBar.h"
36 #include "PoolManager.h"
37 #include "GameEventMgr.h"
40 #include "AccountMgr.h"
41 #include "MapPersistentStateMgr.h"
42 #include "SpellAuras.h"
44 #include "WaypointManager.h"
45 #include "GossipDef.h"
47 #include "InstanceData.h"
48 #include "DB2Structure.h"
49 #include "DB2Stores.h"
53 INSTANTIATE_SINGLETON_1(ObjectMgr
);
55 bool normalizePlayerName(std::string
& name
)
60 wchar_t wstr_buf
[MAX_INTERNAL_PLAYER_NAME
+ 1];
61 size_t wstr_len
= MAX_INTERNAL_PLAYER_NAME
;
63 if (!Utf8toWStr(name
, &wstr_buf
[0], wstr_len
))
66 wstr_buf
[0] = wcharToUpper(wstr_buf
[0]);
67 for (size_t i
= 1; i
< wstr_len
; ++i
)
68 wstr_buf
[i
] = wcharToLower(wstr_buf
[i
]);
70 if (!WStrToUtf8(wstr_buf
, wstr_len
, name
))
76 LanguageDesc lang_description
[LANGUAGES_COUNT
] =
79 { LANG_UNIVERSAL
, 0, 0 },
80 { LANG_ORCISH
, 669, SKILL_LANG_ORCISH
},
81 { LANG_DARNASSIAN
, 671, SKILL_LANG_DARNASSIAN
},
82 { LANG_TAURAHE
, 670, SKILL_LANG_TAURAHE
},
83 { LANG_DWARVISH
, 672, SKILL_LANG_DWARVEN
},
84 { LANG_COMMON
, 668, SKILL_LANG_COMMON
},
85 { LANG_DEMONIC
, 815, SKILL_LANG_DEMON_TONGUE
},
86 { LANG_TITAN
, 816, SKILL_LANG_TITAN
},
87 { LANG_THALASSIAN
, 813, SKILL_LANG_THALASSIAN
},
88 { LANG_DRACONIC
, 814, SKILL_LANG_DRACONIC
},
89 { LANG_KALIMAG
, 817, SKILL_LANG_OLD_TONGUE
},
90 { LANG_GNOMISH
, 7340, SKILL_LANG_GNOMISH
},
91 { LANG_TROLL
, 7341, SKILL_LANG_TROLL
},
92 { LANG_GUTTERSPEAK
, 17737, SKILL_LANG_GUTTERSPEAK
},
93 { LANG_DRAENEI
, 29932, SKILL_LANG_DRAENEI
},
94 { LANG_ZOMBIE
, 0, 0 },
95 { LANG_GNOMISH_BINARY
, 0, 0 },
96 { LANG_GOBLIN_BINARY
, 0, 0 },
97 { LANG_WORGEN
, 69270, SKILL_LANG_WORGEN
},
98 { LANG_GOBLIN
, 69269, SKILL_LANG_GOBLIN
}
101 LanguageDesc
const* GetLanguageDescByID(uint32 lang
)
103 for (int i
= 0; i
< LANGUAGES_COUNT
; ++i
)
105 if (uint32(lang_description
[i
].lang_id
) == lang
)
106 return &lang_description
[i
];
112 bool SpellClickInfo::IsFitToRequirements(Player
const* player
) const
116 // not in expected required quest state
117 if (!player
|| ((!questStartCanActive
|| !player
->IsActiveQuest(questStart
)) && !player
->GetQuestRewardStatus(questStart
)))
123 // not in expected forbidden quest state
124 if (!player
|| player
->GetQuestRewardStatus(questEnd
))
132 T IdGenerator
<T
>::Generate()
134 if (m_nextGuid
>= std::numeric_limits
<T
>::max() - 1)
136 sLog
.outError("%s guid overflow!! Can't continue, shutting down server. ", m_name
);
137 World::StopNow(ERROR_EXIT_CODE
);
142 template uint32 IdGenerator
<uint32
>::Generate();
143 template uint64 IdGenerator
<uint64
>::Generate();
145 ObjectMgr::ObjectMgr() :
146 m_ArenaTeamIds("Arena team ids"),
147 m_AuctionIds("Auction ids"),
148 m_EquipmentSetIds("Equipment set ids"),
149 m_GuildIds("Guild ids"),
150 m_MailIds("Mail ids"),
151 m_PetNumbers("Pet numbers"),
152 m_FirstTemporaryCreatureGuid(1),
153 m_FirstTemporaryGameObjectGuid(1)
157 ObjectMgr::~ObjectMgr()
159 for (QuestMap::iterator i
= mQuestTemplates
.begin(); i
!= mQuestTemplates
.end(); ++i
)
162 for (PetLevelInfoMap::iterator i
= petInfo
.begin(); i
!= petInfo
.end(); ++i
)
165 for (int race
= 0; race
< MAX_RACES
; ++race
)
166 for (int class_
= 0; class_
< MAX_CLASSES
; ++class_
)
167 delete[] playerInfo
[race
][class_
].levelInfo
;
170 for (GroupMap::iterator itr
= mGroupMap
.begin(); itr
!= mGroupMap
.end(); ++itr
)
173 for (ArenaTeamMap::iterator itr
= mArenaTeamMap
.begin(); itr
!= mArenaTeamMap
.end(); ++itr
)
176 for (CacheVendorItemMap::iterator itr
= m_mCacheVendorTemplateItemMap
.begin(); itr
!= m_mCacheVendorTemplateItemMap
.end(); ++itr
)
179 for (CacheVendorItemMap::iterator itr
= m_mCacheVendorItemMap
.begin(); itr
!= m_mCacheVendorItemMap
.end(); ++itr
)
182 for (CacheTrainerSpellMap::iterator itr
= m_mCacheTrainerSpellMap
.begin(); itr
!= m_mCacheTrainerSpellMap
.end(); ++itr
)
186 Group
* ObjectMgr::GetGroupById(uint32 id
) const
188 GroupMap::const_iterator itr
= mGroupMap
.find(id
);
189 if (itr
!= mGroupMap
.end())
195 ArenaTeam
* ObjectMgr::GetArenaTeamById(uint32 arenateamid
) const
197 ArenaTeamMap::const_iterator itr
= mArenaTeamMap
.find(arenateamid
);
198 if (itr
!= mArenaTeamMap
.end())
204 ArenaTeam
* ObjectMgr::GetArenaTeamByName(const std::string
& arenateamname
) const
206 for (ArenaTeamMap::const_iterator itr
= mArenaTeamMap
.begin(); itr
!= mArenaTeamMap
.end(); ++itr
)
207 if (itr
->second
->GetName() == arenateamname
)
213 ArenaTeam
* ObjectMgr::GetArenaTeamByCaptain(ObjectGuid guid
) const
215 for (ArenaTeamMap::const_iterator itr
= mArenaTeamMap
.begin(); itr
!= mArenaTeamMap
.end(); ++itr
)
216 if (itr
->second
->GetCaptainGuid() == guid
)
222 void ObjectMgr::LoadCreatureLocales()
224 mCreatureLocaleMap
.clear(); // need for reload case
226 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,name_loc1,subname_loc1,name_loc2,subname_loc2,name_loc3,subname_loc3,name_loc4,subname_loc4,name_loc5,subname_loc5,name_loc6,subname_loc6,name_loc7,subname_loc7,name_loc8,subname_loc8 FROM locales_creature");
235 sLog
.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
239 BarGoLink
bar(result
->GetRowCount());
243 Field
* fields
= result
->Fetch();
246 uint32 entry
= fields
[0].GetUInt32();
248 if (!GetCreatureTemplate(entry
))
250 ERROR_DB_STRICT_LOG("Table `locales_creature` has data for not existed creature entry %u, skipped.", entry
);
254 CreatureLocale
& data
= mCreatureLocaleMap
[entry
];
256 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
258 std::string str
= fields
[1 + 2 * (i
- 1)].GetCppString();
261 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
264 if ((int32
)data
.Name
.size() <= idx
)
265 data
.Name
.resize(idx
+ 1);
267 data
.Name
[idx
] = str
;
270 str
= fields
[1 + 2 * (i
- 1) + 1].GetCppString();
273 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
276 if ((int32
)data
.SubName
.size() <= idx
)
277 data
.SubName
.resize(idx
+ 1);
279 data
.SubName
[idx
] = str
;
284 while (result
->NextRow());
289 sLog
.outString(">> Loaded " SIZEFMTD
" creature locale strings", mCreatureLocaleMap
.size());
292 void ObjectMgr::LoadGossipMenuItemsLocales()
294 mGossipMenuItemsLocaleMap
.clear(); // need for reload case
296 QueryResult
* result
= WorldDatabase
.Query("SELECT menu_id,id,"
297 "option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
298 "option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
299 "option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
300 "option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
301 "FROM locales_gossip_menu_option");
310 sLog
.outString(">> Loaded 0 gossip_menu_option locale strings. DB table `locales_gossip_menu_option` is empty.");
314 BarGoLink
bar(result
->GetRowCount());
318 Field
* fields
= result
->Fetch();
321 uint16 menuId
= fields
[0].GetUInt16();
322 uint16 id
= fields
[1].GetUInt16();
324 GossipMenuItemsMapBounds bounds
= GetGossipMenuItemsMapBounds(menuId
);
327 if (bounds
.first
!= bounds
.second
)
329 for (GossipMenuItemsMap::const_iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
331 if (itr
->second
.id
== id
)
341 ERROR_DB_STRICT_LOG("Table `locales_gossip_menu_option` has data for nonexistent gossip menu %u item %u, skipped.", menuId
, id
);
345 GossipMenuItemsLocale
& data
= mGossipMenuItemsLocaleMap
[MAKE_PAIR32(menuId
, id
)];
347 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
349 std::string str
= fields
[2 + 2 * (i
- 1)].GetCppString();
352 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
355 if ((int32
)data
.OptionText
.size() <= idx
)
356 data
.OptionText
.resize(idx
+ 1);
358 data
.OptionText
[idx
] = str
;
361 str
= fields
[2 + 2 * (i
- 1) + 1].GetCppString();
364 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
367 if ((int32
)data
.BoxText
.size() <= idx
)
368 data
.BoxText
.resize(idx
+ 1);
370 data
.BoxText
[idx
] = str
;
375 while (result
->NextRow());
380 sLog
.outString(">> Loaded " SIZEFMTD
" gossip_menu_option locale strings", mGossipMenuItemsLocaleMap
.size());
383 void ObjectMgr::LoadPointOfInterestLocales()
385 mPointOfInterestLocaleMap
.clear(); // need for reload case
387 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,icon_name_loc1,icon_name_loc2,icon_name_loc3,icon_name_loc4,icon_name_loc5,icon_name_loc6,icon_name_loc7,icon_name_loc8 FROM locales_points_of_interest");
396 sLog
.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty.");
400 BarGoLink
bar(result
->GetRowCount());
404 Field
* fields
= result
->Fetch();
407 uint32 entry
= fields
[0].GetUInt32();
409 if (!GetPointOfInterest(entry
))
411 ERROR_DB_STRICT_LOG("Table `locales_points_of_interest` has data for nonexistent POI entry %u, skipped.", entry
);
415 PointOfInterestLocale
& data
= mPointOfInterestLocaleMap
[entry
];
417 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
419 std::string str
= fields
[i
].GetCppString();
423 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
426 if ((int32
)data
.IconName
.size() <= idx
)
427 data
.IconName
.resize(idx
+ 1);
429 data
.IconName
[idx
] = str
;
433 while (result
->NextRow());
438 sLog
.outString(">> Loaded " SIZEFMTD
" points_of_interest locale strings", mPointOfInterestLocaleMap
.size());
441 struct SQLCreatureLoader
: public SQLStorageLoaderBase
<SQLCreatureLoader
, SQLStorage
>
444 void convert_from_str(uint32
/*field_pos*/, char const* src
, D
& dst
)
446 dst
= D(sScriptMgr
.GetScriptId(src
));
450 void ObjectMgr::LoadCreatureTemplates()
452 SQLCreatureLoader loader
;
453 loader
.Load(sCreatureStorage
);
455 sLog
.outString(">> Loaded %u creature definitions", sCreatureStorage
.GetRecordCount());
458 std::set
<uint32
> difficultyEntries
[MAX_DIFFICULTY
- 1]; // already loaded difficulty 1 value in creatures
459 std::set
<uint32
> hasDifficultyEntries
[MAX_DIFFICULTY
- 1]; // already loaded creatures with difficulty 1 values
461 // check data correctness
462 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
464 CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
);
468 bool ok
= true; // bool to allow continue outside this loop
469 for (uint32 diff
= 0; diff
< MAX_DIFFICULTY
- 1 && ok
; ++diff
)
471 if (!cInfo
->DifficultyEntry
[diff
])
473 ok
= false; // will be set to true at the end of this loop again
475 CreatureInfo
const* difficultyInfo
= GetCreatureTemplate(cInfo
->DifficultyEntry
[diff
]);
478 sLog
.outErrorDb("Creature (Entry: %u) have `difficulty_entry_%u`=%u but creature entry %u not exist.",
479 i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
], cInfo
->DifficultyEntry
[diff
]);
483 if (difficultyEntries
[diff
].find(i
) != difficultyEntries
[diff
].end())
485 sLog
.outErrorDb("Creature (Entry: %u) listed as difficulty %u but have value in `difficulty_entry_%u`.", i
, diff
+ 1, diff
+ 1);
490 for (uint32 diff2
= 0; diff2
< MAX_DIFFICULTY
- 1 && ok2
; ++diff2
)
493 if (difficultyEntries
[diff2
].find(cInfo
->DifficultyEntry
[diff
]) != difficultyEntries
[diff2
].end())
495 sLog
.outErrorDb("Creature (Entry: %u) already listed as difficulty %u for another entry.", cInfo
->DifficultyEntry
[diff
], diff2
+ 1);
499 if (hasDifficultyEntries
[diff2
].find(cInfo
->DifficultyEntry
[diff
]) != hasDifficultyEntries
[diff2
].end())
501 sLog
.outErrorDb("Creature (Entry: %u) have `difficulty_entry_%u`=%u but creature entry %u have difficulty %u entry also.",
502 i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
], cInfo
->DifficultyEntry
[diff
], diff2
+ 1);
510 if (cInfo
->unit_class
!= difficultyInfo
->unit_class
)
512 sLog
.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in difficulty %u mode (Entry: %u, class %u).",
513 i
, cInfo
->unit_class
, diff
+ 1, cInfo
->DifficultyEntry
[diff
], difficultyInfo
->unit_class
);
517 if (cInfo
->npcflag
!= difficultyInfo
->npcflag
)
519 sLog
.outErrorDb("Creature (Entry: %u) has different `npcflag` in difficulty %u mode (Entry: %u).", i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
]);
523 if (cInfo
->trainer_class
!= difficultyInfo
->trainer_class
)
525 sLog
.outErrorDb("Creature (Entry: %u) has different `trainer_class` in difficulty %u mode (Entry: %u).", i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
]);
529 if (cInfo
->trainer_race
!= difficultyInfo
->trainer_race
)
531 sLog
.outErrorDb("Creature (Entry: %u) has different `trainer_race` in difficulty %u mode (Entry: %u).", i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
]);
535 if (cInfo
->trainer_type
!= difficultyInfo
->trainer_type
)
537 sLog
.outErrorDb("Creature (Entry: %u) has different `trainer_type` in difficulty %u mode (Entry: %u).", i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
]);
541 if (cInfo
->trainer_spell
!= difficultyInfo
->trainer_spell
)
543 sLog
.outErrorDb("Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", i
, diff
+ 1, cInfo
->DifficultyEntry
[diff
]);
547 if (difficultyInfo
->AIName
&& *difficultyInfo
->AIName
)
549 sLog
.outErrorDb("Difficulty %u mode creature (Entry: %u) has `AIName`, but in any case will used difficulty 0 mode creature (Entry: %u) AIName.",
550 diff
+ 1, cInfo
->DifficultyEntry
[diff
], i
);
554 if (difficultyInfo
->ScriptID
)
556 sLog
.outErrorDb("Difficulty %u mode creature (Entry: %u) has `ScriptName`, but in any case will used difficulty 0 mode creature (Entry: %u) ScriptName.",
557 diff
+ 1, cInfo
->DifficultyEntry
[diff
], i
);
561 hasDifficultyEntries
[diff
].insert(i
);
562 difficultyEntries
[diff
].insert(cInfo
->DifficultyEntry
[diff
]);
568 FactionTemplateEntry
const* factionTemplate
= sFactionTemplateStore
.LookupEntry(cInfo
->faction_A
);
569 if (!factionTemplate
)
570 sLog
.outErrorDb("Creature (Entry: %u) has nonexistent faction_A template (%u)", cInfo
->Entry
, cInfo
->faction_A
);
572 factionTemplate
= sFactionTemplateStore
.LookupEntry(cInfo
->faction_H
);
573 if (!factionTemplate
)
574 sLog
.outErrorDb("Creature (Entry: %u) has nonexistent faction_H template (%u)", cInfo
->Entry
, cInfo
->faction_H
);
576 for (int k
= 0; k
< MAX_KILL_CREDIT
; ++k
)
578 if (cInfo
->KillCredit
[k
])
580 if (!GetCreatureTemplate(cInfo
->KillCredit
[k
]))
582 sLog
.outErrorDb("Creature (Entry: %u) has nonexistent creature entry in `KillCredit%d` (%u)", cInfo
->Entry
, k
+ 1, cInfo
->KillCredit
[k
]);
583 const_cast<CreatureInfo
*>(cInfo
)->KillCredit
[k
] = 0;
588 // used later for scale
589 CreatureDisplayInfoEntry
const* displayScaleEntry
= NULL
;
591 for (int i
= 0; i
< MAX_CREATURE_MODEL
; ++i
)
593 if (cInfo
->ModelId
[i
])
595 CreatureDisplayInfoEntry
const* displayEntry
= sCreatureDisplayInfoStore
.LookupEntry(cInfo
->ModelId
[i
]);
598 sLog
.outErrorDb("Creature (Entry: %u) has nonexistent modelid_%d (%u), can crash client", cInfo
->Entry
, i
+ 1, cInfo
->ModelId
[i
]);
599 const_cast<CreatureInfo
*>(cInfo
)->ModelId
[i
] = 0;
601 else if (!displayScaleEntry
)
602 displayScaleEntry
= displayEntry
;
604 CreatureModelInfo
const* minfo
= sCreatureModelStorage
.LookupEntry
<CreatureModelInfo
>(cInfo
->ModelId
[i
]);
606 sLog
.outErrorDb("Creature (Entry: %u) are using modelid_%d (%u), but creature_model_info are missing for this model.", cInfo
->Entry
, i
+ 1, cInfo
->ModelId
[i
]);
610 if (!displayScaleEntry
)
611 sLog
.outErrorDb("Creature (Entry: %u) has nonexistent modelid in modelid_1/modelid_2/modelid_3/modelid_4", cInfo
->Entry
);
613 // use below code for 0-checks for unit_class
614 if (!cInfo
->unit_class
)
615 ERROR_DB_STRICT_LOG("Creature (Entry: %u) not has proper unit_class(%u) for creature_template", cInfo
->Entry
, cInfo
->unit_class
);
616 else if (((1 << (cInfo
->unit_class
- 1)) & CLASSMASK_ALL_CREATURES
) == 0)
617 sLog
.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo
->Entry
, cInfo
->unit_class
);
619 if (cInfo
->dmgschool
>= MAX_SPELL_SCHOOL
)
621 sLog
.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`", cInfo
->Entry
, cInfo
->dmgschool
);
622 const_cast<CreatureInfo
*>(cInfo
)->dmgschool
= SPELL_SCHOOL_NORMAL
;
625 if (cInfo
->baseattacktime
== 0)
626 const_cast<CreatureInfo
*>(cInfo
)->baseattacktime
= BASE_ATTACK_TIME
;
628 if (cInfo
->rangeattacktime
== 0)
629 const_cast<CreatureInfo
*>(cInfo
)->rangeattacktime
= BASE_ATTACK_TIME
;
631 if (cInfo
->npcflag
& UNIT_NPC_FLAG_SPELLCLICK
)
633 sLog
.outDebug("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, but it is expected to be set in run-time based at `npc_spellclick_spells` contents.", cInfo
->Entry
, UNIT_NPC_FLAG_SPELLCLICK
);
634 const_cast<CreatureInfo
*>(cInfo
)->npcflag
&= ~UNIT_NPC_FLAG_SPELLCLICK
;
637 if ((cInfo
->npcflag
& UNIT_NPC_FLAG_TRAINER
) && cInfo
->trainer_type
>= MAX_TRAINER_TYPE
)
638 sLog
.outErrorDb("Creature (Entry: %u) has wrong trainer type %u", cInfo
->Entry
, cInfo
->trainer_type
);
640 if (cInfo
->type
&& !sCreatureTypeStore
.LookupEntry(cInfo
->type
))
642 sLog
.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`", cInfo
->Entry
, cInfo
->type
);
643 const_cast<CreatureInfo
*>(cInfo
)->type
= CREATURE_TYPE_HUMANOID
;
646 // must exist or used hidden but used in data horse case
647 if (cInfo
->family
&& !sCreatureFamilyStore
.LookupEntry(cInfo
->family
) && cInfo
->family
!= CREATURE_FAMILY_HORSE_CUSTOM
)
649 sLog
.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`", cInfo
->Entry
, cInfo
->family
);
650 const_cast<CreatureInfo
*>(cInfo
)->family
= 0;
653 if (cInfo
->InhabitType
<= 0 || cInfo
->InhabitType
> INHABIT_ANYWHERE
)
655 sLog
.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly", cInfo
->Entry
, cInfo
->InhabitType
);
656 const_cast<CreatureInfo
*>(cInfo
)->InhabitType
= INHABIT_ANYWHERE
;
659 if (cInfo
->PetSpellDataId
)
661 CreatureSpellDataEntry
const* spellDataId
= sCreatureSpellDataStore
.LookupEntry(cInfo
->PetSpellDataId
);
663 sLog
.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo
->Entry
, cInfo
->PetSpellDataId
);
666 if (cInfo
->MovementType
>= MAX_DB_MOTION_TYPE
)
668 sLog
.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.", cInfo
->Entry
, cInfo
->MovementType
);
669 const_cast<CreatureInfo
*>(cInfo
)->MovementType
= IDLE_MOTION_TYPE
;
672 if (cInfo
->vehicleId
&& !sVehicleStore
.LookupEntry(cInfo
->vehicleId
))
674 sLog
.outErrorDb("Creature (Entry: %u) has non-existing vehicle_id (%u), set to 0.", cInfo
->Entry
, cInfo
->vehicleId
);
675 const_cast<CreatureInfo
*>(cInfo
)->vehicleId
= 0;
678 if (cInfo
->equipmentId
> 0) // 0 no equipment
680 if (!GetEquipmentInfo(cInfo
->equipmentId
))
682 sLog
.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", cInfo
->Entry
, cInfo
->equipmentId
);
683 const_cast<CreatureInfo
*>(cInfo
)->equipmentId
= 0;
687 if (cInfo
->vendorId
> 0)
689 if (!(cInfo
->npcflag
& UNIT_NPC_FLAG_VENDOR
))
690 sLog
.outErrorDb("Table `creature_template` have creature (Entry: %u) with vendor_id %u but not have flag UNIT_NPC_FLAG_VENDOR (%u), vendor items will ignored.", cInfo
->Entry
, cInfo
->vendorId
, UNIT_NPC_FLAG_VENDOR
);
693 /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
694 if (cInfo
->scale
<= 0.0f
)
696 if (displayScaleEntry
)
697 const_cast<CreatureInfo
*>(cInfo
)->scale
= displayScaleEntry
->scale
;
699 const_cast<CreatureInfo
*>(cInfo
)->scale
= DEFAULT_OBJECT_SCALE
;
704 void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon
* addon
, char const* table
, char const* guidEntryStr
)
706 // Now add the auras, format "spell1 spell2 ..."
708 std::vector
<int> val
;
709 s
= p
= (char*)reinterpret_cast<char const*>(addon
->auras
);
717 val
.push_back(atoi(s
));
722 val
.push_back(atoi(s
));
724 // free char* loaded memory
725 delete[](char*)reinterpret_cast<char const*>(addon
->auras
);
735 // replace by new structures array
736 const_cast<uint32
*&>(addon
->auras
) = new uint32
[val
.size() + 1];
739 for (uint32 j
= 0; j
< val
.size(); ++j
)
741 uint32
& cAura
= const_cast<uint32
&>(addon
->auras
[i
]);
742 cAura
= uint32(val
[j
]);
744 SpellEntry
const* AdditionalSpellInfo
= sSpellStore
.LookupEntry(cAura
);
745 if (!AdditionalSpellInfo
)
747 sLog
.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.", guidEntryStr
, addon
->guidOrEntry
, cAura
, table
);
751 if (std::find(&addon
->auras
[0], &addon
->auras
[i
], cAura
) != &addon
->auras
[i
])
753 sLog
.outErrorDb("Creature (%s: %u) has duplicate spell %u defined in `auras` field in `%s`.", guidEntryStr
, addon
->guidOrEntry
, cAura
, table
);
760 // fill terminator element (after last added)
761 const_cast<uint32
&>(addon
->auras
[i
]) = 0;
764 void ObjectMgr::LoadCreatureAddons(SQLStorage
& creatureaddons
, char const* entryName
, char const* comment
)
766 creatureaddons
.Load();
768 sLog
.outString(">> Loaded %u %s", creatureaddons
.GetRecordCount(), comment
);
771 // check data correctness and convert 'auras'
772 for (uint32 i
= 1; i
< creatureaddons
.GetMaxEntry(); ++i
)
774 CreatureDataAddon
const* addon
= creatureaddons
.LookupEntry
<CreatureDataAddon
>(i
);
780 if (!sCreatureDisplayInfoStore
.LookupEntry(addon
->mount
))
782 sLog
.outErrorDb("Creature (%s %u) have invalid displayInfoId for mount (%u) defined in `%s`.", entryName
, addon
->guidOrEntry
, addon
->mount
, creatureaddons
.GetTableName());
783 const_cast<CreatureDataAddon
*>(addon
)->mount
= 0;
787 if (addon
->sheath_state
> SHEATH_STATE_RANGED
)
788 sLog
.outErrorDb("Creature (%s %u) has unknown sheath state (%u) defined in `%s`.", entryName
, addon
->guidOrEntry
, addon
->sheath_state
, creatureaddons
.GetTableName());
790 if (!sEmotesStore
.LookupEntry(addon
->emote
))
792 sLog
.outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName
, addon
->guidOrEntry
, addon
->emote
, creatureaddons
.GetTableName());
793 const_cast<CreatureDataAddon
*>(addon
)->emote
= 0;
796 ConvertCreatureAddonAuras(const_cast<CreatureDataAddon
*>(addon
), creatureaddons
.GetTableName(), entryName
);
800 void ObjectMgr::LoadCreatureAddons()
802 LoadCreatureAddons(sCreatureInfoAddonStorage
, "Entry", "creature template addons");
805 for (uint32 i
= 1; i
< sCreatureInfoAddonStorage
.GetMaxEntry(); ++i
)
806 if (CreatureDataAddon
const* addon
= sCreatureInfoAddonStorage
.LookupEntry
<CreatureDataAddon
>(i
))
807 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(addon
->guidOrEntry
))
808 sLog
.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`", addon
->guidOrEntry
, sCreatureInfoAddonStorage
.GetTableName());
810 LoadCreatureAddons(sCreatureDataAddonStorage
, "GUID", "creature addons");
813 for (uint32 i
= 1; i
< sCreatureDataAddonStorage
.GetMaxEntry(); ++i
)
814 if (CreatureDataAddon
const* addon
= sCreatureDataAddonStorage
.LookupEntry
<CreatureDataAddon
>(i
))
815 if (mCreatureDataMap
.find(addon
->guidOrEntry
) == mCreatureDataMap
.end())
816 sLog
.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`", addon
->guidOrEntry
);
819 void ObjectMgr::LoadEquipmentTemplates()
821 sEquipmentStorage
.Load();
823 for (uint32 i
= 0; i
< sEquipmentStorage
.GetMaxEntry(); ++i
)
825 EquipmentInfo
const* eqInfo
= sEquipmentStorage
.LookupEntry
<EquipmentInfo
>(i
);
830 for (uint8 j
= 0; j
< 3; ++j
)
832 if (!eqInfo
->equipentry
[j
])
835 ItemEntry
const *dbcitem
= sItemStore
.LookupEntry(eqInfo
->equipentry
[j
]);
839 sLog
.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo
->equipentry
[j
], j
+1, i
);
840 const_cast<EquipmentInfo
*>(eqInfo
)->equipentry
[j
] = 0;
844 if (dbcitem
->InventoryType
!= INVTYPE_WEAPON
&&
845 dbcitem
->InventoryType
!= INVTYPE_SHIELD
&&
846 dbcitem
->InventoryType
!= INVTYPE_RANGED
&&
847 dbcitem
->InventoryType
!= INVTYPE_2HWEAPON
&&
848 dbcitem
->InventoryType
!= INVTYPE_WEAPONMAINHAND
&&
849 dbcitem
->InventoryType
!= INVTYPE_WEAPONOFFHAND
&&
850 dbcitem
->InventoryType
!= INVTYPE_HOLDABLE
&&
851 dbcitem
->InventoryType
!= INVTYPE_THROWN
&&
852 dbcitem
->InventoryType
!= INVTYPE_RANGEDRIGHT
&&
853 dbcitem
->InventoryType
!= INVTYPE_RELIC
)
855 sLog
.outErrorDb("Item (entry=%u) in creature_equip_template.equipentry%u for entry = %u is not equipable in a hand, forced to 0.", eqInfo
->equipentry
[j
], j
+1, i
);
856 const_cast<EquipmentInfo
*>(eqInfo
)->equipentry
[j
] = 0;
861 sLog
.outString(">> Loaded %u equipment template", sEquipmentStorage
.GetRecordCount());
865 // generally models that does not have a gender(2), or has alternative model for same gender
866 uint32
ObjectMgr::GetCreatureModelAlternativeModel(uint32 modelId
) const
868 if (const CreatureModelInfo
* modelInfo
= GetCreatureModelInfo(modelId
))
869 return modelInfo
->modelid_alternative
;
874 CreatureModelInfo
const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id
) const
876 CreatureModelInfo
const* minfo
= GetCreatureModelInfo(display_id
);
880 // If a model for another gender exists, 50% chance to use it
881 if (minfo
->modelid_other_gender
!= 0 && urand(0, 1) == 0)
883 CreatureModelInfo
const* minfo_tmp
= GetCreatureModelInfo(minfo
->modelid_other_gender
);
886 sLog
.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo
->modelid
, minfo
->modelid_other_gender
);
887 return minfo
; // not fatal, just use the previous one
896 uint32
ObjectMgr::GetModelForRace(uint32 sourceModelId
, uint32 racemask
)
900 CreatureModelRaceMapBounds bounds
= m_mCreatureModelRaceMap
.equal_range(sourceModelId
);
902 for (CreatureModelRaceMap::const_iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
904 if (!(itr
->second
.racemask
& racemask
))
907 if (itr
->second
.creature_entry
)
909 const CreatureInfo
* cInfo
= GetCreatureTemplate(itr
->second
.creature_entry
);
910 modelId
= Creature::ChooseDisplayId(cInfo
);
914 modelId
= itr
->second
.modelid_racial
;
921 void ObjectMgr::LoadCreatureModelInfo()
923 sCreatureModelStorage
.Load();
926 for (uint32 i
= 1; i
< sCreatureModelStorage
.GetMaxEntry(); ++i
)
928 CreatureModelInfo
const* minfo
= sCreatureModelStorage
.LookupEntry
<CreatureModelInfo
>(i
);
932 if (!sCreatureDisplayInfoStore
.LookupEntry(minfo
->modelid
))
933 sLog
.outErrorDb("Table `creature_model_info` has model for nonexistent model id (%u).", minfo
->modelid
);
935 if (minfo
->gender
>= MAX_GENDER
)
937 sLog
.outErrorDb("Table `creature_model_info` has invalid gender (%u) for model id (%u).", uint32(minfo
->gender
), minfo
->modelid
);
938 const_cast<CreatureModelInfo
*>(minfo
)->gender
= GENDER_MALE
;
941 if (minfo
->modelid_other_gender
)
943 if (minfo
->modelid_other_gender
== minfo
->modelid
)
945 sLog
.outErrorDb("Table `creature_model_info` has redundant modelid_other_gender model (%u) defined for model id %u.", minfo
->modelid_other_gender
, minfo
->modelid
);
946 const_cast<CreatureModelInfo
*>(minfo
)->modelid_other_gender
= 0;
948 else if (!sCreatureDisplayInfoStore
.LookupEntry(minfo
->modelid_other_gender
))
950 sLog
.outErrorDb("Table `creature_model_info` has nonexistent modelid_other_gender model (%u) defined for model id %u.", minfo
->modelid_other_gender
, minfo
->modelid
);
951 const_cast<CreatureModelInfo
*>(minfo
)->modelid_other_gender
= 0;
955 if (minfo
->modelid_alternative
)
957 if (minfo
->modelid_alternative
== minfo
->modelid
)
959 sLog
.outErrorDb("Table `creature_model_info` has redundant modelid_alternative model (%u) defined for model id %u.", minfo
->modelid_alternative
, minfo
->modelid
);
960 const_cast<CreatureModelInfo
*>(minfo
)->modelid_alternative
= 0;
962 else if (!sCreatureDisplayInfoStore
.LookupEntry(minfo
->modelid_alternative
))
964 sLog
.outErrorDb("Table `creature_model_info` has nonexistent modelid_alternative model (%u) defined for model id %u.", minfo
->modelid_alternative
, minfo
->modelid
);
965 const_cast<CreatureModelInfo
*>(minfo
)->modelid_alternative
= 0;
970 // character races expected have model info data in table
971 for (uint32 race
= 1; race
< sChrRacesStore
.GetNumRows(); ++race
)
973 ChrRacesEntry
const* raceEntry
= sChrRacesStore
.LookupEntry(race
);
977 if (!((1 << (race
- 1)) & RACEMASK_ALL_PLAYABLE
))
980 if (CreatureModelInfo
const* minfo
= GetCreatureModelInfo(raceEntry
->model_f
))
982 if (minfo
->gender
!= GENDER_FEMALE
)
983 sLog
.outErrorDb("Table `creature_model_info` have wrong gender %u for character race %u female model id %u", minfo
->gender
, race
, raceEntry
->model_f
);
985 if (minfo
->modelid_other_gender
!= raceEntry
->model_m
)
986 sLog
.outErrorDb("Table `creature_model_info` have wrong other gender model id %u for character race %u female model id %u", minfo
->modelid_other_gender
, race
, raceEntry
->model_f
);
988 if (minfo
->bounding_radius
<= 0.0f
)
990 sLog
.outErrorDb("Table `creature_model_info` have wrong bounding_radius %f for character race %u female model id %u, use %f instead", minfo
->bounding_radius
, race
, raceEntry
->model_f
, DEFAULT_WORLD_OBJECT_SIZE
);
991 const_cast<CreatureModelInfo
*>(minfo
)->bounding_radius
= DEFAULT_WORLD_OBJECT_SIZE
;
994 if (minfo
->combat_reach
!= 1.5f
)
996 sLog
.outErrorDb("Table `creature_model_info` have wrong combat_reach %f for character race %u female model id %u, expected always 1.5f", minfo
->combat_reach
, race
, raceEntry
->model_f
);
997 const_cast<CreatureModelInfo
*>(minfo
)->combat_reach
= 1.5f
;
1001 sLog
.outErrorDb("Table `creature_model_info` expect have data for character race %u female model id %u", race
, raceEntry
->model_f
);
1003 if (CreatureModelInfo
const* minfo
= GetCreatureModelInfo(raceEntry
->model_m
))
1005 if (minfo
->gender
!= GENDER_MALE
)
1006 sLog
.outErrorDb("Table `creature_model_info` have wrong gender %u for character race %u male model id %u", minfo
->gender
, race
, raceEntry
->model_m
);
1008 if (minfo
->modelid_other_gender
!= raceEntry
->model_f
)
1009 sLog
.outErrorDb("Table `creature_model_info` have wrong other gender model id %u for character race %u male model id %u", minfo
->modelid_other_gender
, race
, raceEntry
->model_m
);
1011 if (minfo
->bounding_radius
<= 0.0f
)
1013 sLog
.outErrorDb("Table `creature_model_info` have wrong bounding_radius %f for character race %u male model id %u, use %f instead", minfo
->bounding_radius
, race
, raceEntry
->model_f
, DEFAULT_WORLD_OBJECT_SIZE
);
1014 const_cast<CreatureModelInfo
*>(minfo
)->bounding_radius
= DEFAULT_WORLD_OBJECT_SIZE
;
1017 if (minfo
->combat_reach
!= 1.5f
)
1019 sLog
.outErrorDb("Table `creature_model_info` have wrong combat_reach %f for character race %u male model id %u, expected always 1.5f", minfo
->combat_reach
, race
, raceEntry
->model_m
);
1020 const_cast<CreatureModelInfo
*>(minfo
)->combat_reach
= 1.5f
;
1024 sLog
.outErrorDb("Table `creature_model_info` expect have data for character race %u male model id %u", race
, raceEntry
->model_m
);
1028 sLog
.outString(">> Loaded %u creature model based info", sCreatureModelStorage
.GetRecordCount());
1032 void ObjectMgr::LoadCreatureModelRace()
1034 m_mCreatureModelRaceMap
.clear(); // can be used for reload
1036 QueryResult
* result
= WorldDatabase
.Query("SELECT modelid, racemask, creature_entry, modelid_racial FROM creature_model_race");
1045 sLog
.outErrorDb(">> Loaded creature_model_race, table is empty!");
1049 BarGoLink
bar(result
->GetRowCount());
1054 std::map
<uint32
, uint32
> model2raceMask
;
1060 Field
* fields
= result
->Fetch();
1062 CreatureModelRace raceData
;
1064 raceData
.modelid
= fields
[0].GetUInt32();
1065 raceData
.racemask
= fields
[1].GetUInt32();
1066 raceData
.creature_entry
= fields
[2].GetUInt32();
1067 raceData
.modelid_racial
= fields
[3].GetUInt32();
1069 if (!sCreatureDisplayInfoStore
.LookupEntry(raceData
.modelid
))
1071 sLog
.outErrorDb("Table `creature_model_race` has model for nonexistent model id (%u), skipping", raceData
.modelid
);
1075 if (!sCreatureModelStorage
.LookupEntry
<CreatureModelInfo
>(raceData
.modelid
))
1077 sLog
.outErrorDb("Table `creature_model_race` modelid %u does not exist in creature_model_info, skipping", raceData
.modelid
);
1081 if (!raceData
.racemask
)
1083 sLog
.outErrorDb("Table `creature_model_race` modelid %u has no racemask defined, skipping", raceData
.modelid
);
1087 if (!(raceData
.racemask
& RACEMASK_ALL_PLAYABLE
))
1089 sLog
.outErrorDb("Table `creature_model_race` modelid %u include invalid racemask, skipping", raceData
.modelid
);
1093 std::map
<uint32
, uint32
>::const_iterator model2Race
= model2raceMask
.find(raceData
.modelid
);
1095 // can't have same mask for same model several times
1096 if (model2Race
!= model2raceMask
.end())
1098 if (model2Race
->second
& raceData
.racemask
)
1100 sLog
.outErrorDb("Table `creature_model_race` modelid %u with racemask %u has mask already included for same modelid, skipping", raceData
.modelid
, raceData
.racemask
);
1105 model2raceMask
[raceData
.modelid
] |= raceData
.racemask
;
1107 // creature_entry is the prefered way
1108 if (raceData
.creature_entry
)
1110 if (raceData
.modelid_racial
)
1111 sLog
.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial for modelid %u but a creature_entry are already defined, modelid_racial will never be used.", raceData
.modelid
, raceData
.modelid_racial
);
1113 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(raceData
.creature_entry
))
1115 sLog
.outErrorDb("Table `creature_model_race` modelid %u has creature_entry for nonexistent creature_template (%u), skipping", raceData
.modelid
, raceData
.creature_entry
);
1119 else if (raceData
.modelid_racial
)
1121 if (!sCreatureDisplayInfoStore
.LookupEntry(raceData
.modelid_racial
))
1123 sLog
.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial for nonexistent model id (%u), skipping", raceData
.modelid
, raceData
.modelid_racial
);
1127 if (!sCreatureModelStorage
.LookupEntry
<CreatureModelInfo
>(raceData
.modelid_racial
))
1129 sLog
.outErrorDb("Table `creature_model_race` modelid %u has modelid_racial %u, but are not defined in creature_model_info, skipping", raceData
.modelid
, raceData
.modelid_racial
);
1135 sLog
.outErrorDb("Table `creature_model_race` modelid %u does not have either creature_entry or modelid_racial defined, skipping", raceData
.modelid
);
1139 m_mCreatureModelRaceMap
.insert(CreatureModelRaceMap::value_type(raceData
.modelid
, raceData
));
1143 while (result
->NextRow());
1148 sLog
.outString(">> Loaded %u creature_model_race entries", count
);
1151 void ObjectMgr::LoadCreatures()
1155 QueryResult
* result
= WorldDatabase
.Query("SELECT creature.guid, creature.id, map, modelid,"
1156 // 4 5 6 7 8 9 10 11
1157 "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
1158 // 12 13 14 15 16 17 18
1159 "curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event,"
1161 "pool_creature.pool_entry, pool_creature_template.pool_entry "
1163 "LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
1164 "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid "
1165 "LEFT OUTER JOIN pool_creature_template ON creature.id = pool_creature_template.id");
1174 sLog
.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
1178 // build single time for check creature data
1179 std::set
<uint32
> difficultyCreatures
[MAX_DIFFICULTY
- 1];
1180 for (uint32 i
= 0; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
1181 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1182 for (uint32 diff
= 0; diff
< MAX_DIFFICULTY
- 1; ++diff
)
1183 if (cInfo
->DifficultyEntry
[diff
])
1184 difficultyCreatures
[diff
].insert(cInfo
->DifficultyEntry
[diff
]);
1186 // build single time for check spawnmask
1187 std::map
<uint32
, uint32
> spawnMasks
;
1188 for (uint32 i
= 0; i
< sMapStore
.GetNumRows(); ++i
)
1189 if (sMapStore
.LookupEntry(i
))
1190 for (int k
= 0; k
< MAX_DIFFICULTY
; ++k
)
1191 if (GetMapDifficultyData(i
, Difficulty(k
)))
1192 spawnMasks
[i
] |= (1 << k
);
1193 // Map 0 was removed from dbc as of 4.x.x
1194 spawnMasks
[0] = 1 << REGULAR_DIFFICULTY
;
1196 BarGoLink
bar(result
->GetRowCount());
1200 Field
* fields
= result
->Fetch();
1203 uint32 guid
= fields
[ 0].GetUInt32();
1204 uint32 entry
= fields
[ 1].GetUInt32();
1206 CreatureInfo
const* cInfo
= GetCreatureTemplate(entry
);
1209 sLog
.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid
, entry
);
1213 CreatureData
& data
= mCreatureDataMap
[guid
];
1216 data
.mapid
= fields
[ 2].GetUInt32();
1217 data
.modelid_override
= fields
[ 3].GetUInt32();
1218 data
.equipmentId
= fields
[ 4].GetUInt32();
1219 data
.posX
= fields
[ 5].GetFloat();
1220 data
.posY
= fields
[ 6].GetFloat();
1221 data
.posZ
= fields
[ 7].GetFloat();
1222 data
.orientation
= fields
[ 8].GetFloat();
1223 data
.spawntimesecs
= fields
[ 9].GetUInt32();
1224 data
.spawndist
= fields
[10].GetFloat();
1225 data
.currentwaypoint
= fields
[11].GetUInt32();
1226 data
.curhealth
= fields
[12].GetUInt32();
1227 data
.curmana
= fields
[13].GetUInt32();
1228 data
.is_dead
= fields
[14].GetBool();
1229 data
.movementType
= fields
[15].GetUInt8();
1230 data
.spawnMask
= fields
[16].GetUInt8();
1231 data
.phaseMask
= fields
[17].GetUInt16();
1232 int16 gameEvent
= fields
[18].GetInt16();
1233 int16 GuidPoolId
= fields
[19].GetInt16();
1234 int16 EntryPoolId
= fields
[20].GetInt16();
1236 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(data
.mapid
);
1239 sLog
.outErrorDb("Table `creature` have creature (GUID: %u) that spawned at nonexistent map (Id: %u), skipped.", guid
, data
.mapid
);
1243 if (data
.spawnMask
& ~spawnMasks
[data
.mapid
])
1244 sLog
.outErrorDb("Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u).", guid
, data
.spawnMask
, data
.mapid
);
1247 for (uint32 diff
= 0; diff
< MAX_DIFFICULTY
- 1 && ok
; ++diff
)
1249 if (difficultyCreatures
[diff
].find(data
.id
) != difficultyCreatures
[diff
].end())
1251 sLog
.outErrorDb("Table `creature` have creature (GUID: %u) that listed as difficulty %u template (entry: %u) in `creature_template`, skipped.",
1252 guid
, diff
+ 1, data
.id
);
1259 if (data
.modelid_override
> 0 && !sCreatureDisplayInfoStore
.LookupEntry(data
.modelid_override
))
1261 sLog
.outErrorDb("Table `creature` GUID %u (entry %u) has model for nonexistent model id (%u), set to 0.", guid
, data
.id
, data
.modelid_override
);
1262 data
.modelid_override
= 0;
1265 if (data
.equipmentId
> 0) // -1 no equipment, 0 use default
1267 if (!GetEquipmentInfo(data
.equipmentId
))
1269 sLog
.outErrorDb("Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data
.id
, data
.equipmentId
);
1270 data
.equipmentId
= -1;
1274 if (cInfo
->RegenHealth
&& data
.curhealth
< cInfo
->minhealth
)
1276 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.", guid
, data
.id
, data
.curhealth
, cInfo
->minhealth
);
1277 data
.curhealth
= cInfo
->minhealth
;
1280 if (cInfo
->flags_extra
& CREATURE_FLAG_EXTRA_INSTANCE_BIND
)
1282 if (!mapEntry
|| !mapEntry
->IsDungeon())
1283 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND (%u) but creature are not in instance.",
1284 guid
, data
.id
, CREATURE_FLAG_EXTRA_INSTANCE_BIND
);
1287 if (cInfo
->flags_extra
& CREATURE_FLAG_EXTRA_AGGRO_ZONE
)
1289 if (!mapEntry
|| !mapEntry
->IsDungeon())
1290 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_AGGRO_ZONE (%u) but creature are not in instance.",
1291 guid
, data
.id
, CREATURE_FLAG_EXTRA_AGGRO_ZONE
);
1294 if (data
.curmana
< cInfo
->minmana
)
1296 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.", guid
, data
.id
, data
.curmana
, cInfo
->minmana
);
1297 data
.curmana
= cInfo
->minmana
;
1300 if (data
.spawndist
< 0.0f
)
1302 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid
, data
.id
);
1303 data
.spawndist
= 0.0f
;
1305 else if (data
.movementType
== RANDOM_MOTION_TYPE
)
1307 if (data
.spawndist
== 0.0f
)
1309 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=1 (random movement) but with `spawndist`=0, replace by idle movement type (0).", guid
, data
.id
);
1310 data
.movementType
= IDLE_MOTION_TYPE
;
1313 else if (data
.movementType
== IDLE_MOTION_TYPE
)
1315 if (data
.spawndist
!= 0.0f
)
1317 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid
, data
.id
);
1318 data
.spawndist
= 0.0f
;
1322 if (data
.phaseMask
== 0)
1324 sLog
.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid
, data
.id
);
1328 if (gameEvent
== 0 && GuidPoolId
== 0 && EntryPoolId
== 0) // if not this is to be managed by GameEvent System or Pool system
1329 AddCreatureToGrid(guid
, &data
);
1334 while (result
->NextRow());
1339 sLog
.outString(">> Loaded " SIZEFMTD
" creatures", mCreatureDataMap
.size());
1342 void ObjectMgr::AddCreatureToGrid(uint32 guid
, CreatureData
const* data
)
1344 uint8 mask
= data
->spawnMask
;
1345 for (uint8 i
= 0; mask
!= 0; ++i
, mask
>>= 1)
1349 CellPair cell_pair
= MaNGOS::ComputeCellPair(data
->posX
, data
->posY
);
1350 uint32 cell_id
= (cell_pair
.y_coord
* TOTAL_NUMBER_OF_CELLS_PER_MAP
) + cell_pair
.x_coord
;
1352 CellObjectGuids
& cell_guids
= mMapObjectGuids
[MAKE_PAIR32(data
->mapid
, i
)][cell_id
];
1353 cell_guids
.creatures
.insert(guid
);
1358 void ObjectMgr::RemoveCreatureFromGrid(uint32 guid
, CreatureData
const* data
)
1360 uint8 mask
= data
->spawnMask
;
1361 for (uint8 i
= 0; mask
!= 0; ++i
, mask
>>= 1)
1365 CellPair cell_pair
= MaNGOS::ComputeCellPair(data
->posX
, data
->posY
);
1366 uint32 cell_id
= (cell_pair
.y_coord
* TOTAL_NUMBER_OF_CELLS_PER_MAP
) + cell_pair
.x_coord
;
1368 CellObjectGuids
& cell_guids
= mMapObjectGuids
[MAKE_PAIR32(data
->mapid
, i
)][cell_id
];
1369 cell_guids
.creatures
.erase(guid
);
1374 void ObjectMgr::LoadGameObjects()
1379 QueryResult
* result
= WorldDatabase
.Query("SELECT gameobject.guid, gameobject.id, map, position_x, position_y, position_z, orientation,"
1380 // 7 8 9 10 11 12 13 14 15 16
1381 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event,"
1383 "pool_gameobject.pool_entry, pool_gameobject_template.pool_entry "
1385 "LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
1386 "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid "
1387 "LEFT OUTER JOIN pool_gameobject_template ON gameobject.id = pool_gameobject_template.id");
1396 sLog
.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
1400 // build single time for check spawnmask
1401 std::map
<uint32
, uint32
> spawnMasks
;
1402 for (uint32 i
= 0; i
< sMapStore
.GetNumRows(); ++i
)
1403 if (sMapStore
.LookupEntry(i
))
1404 for (int k
= 0; k
< MAX_DIFFICULTY
; ++k
)
1405 if (GetMapDifficultyData(i
, Difficulty(k
)))
1406 spawnMasks
[i
] |= (1 << k
);
1407 // Map 0 was removed from dbc as of 4.x.x
1408 spawnMasks
[0] = 1 << REGULAR_DIFFICULTY
;
1410 BarGoLink
bar(result
->GetRowCount());
1414 Field
* fields
= result
->Fetch();
1417 uint32 guid
= fields
[ 0].GetUInt32();
1418 uint32 entry
= fields
[ 1].GetUInt32();
1420 GameObjectInfo
const* gInfo
= GetGameObjectInfo(entry
);
1423 sLog
.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid
, entry
);
1427 if (!gInfo
->displayId
)
1429 switch (gInfo
->type
)
1431 // can be invisible always and then not req. display id in like case
1432 case GAMEOBJECT_TYPE_TRAP
:
1433 case GAMEOBJECT_TYPE_SPELL_FOCUS
:
1436 sLog
.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have displayId == 0 and then will always invisible in game.", guid
, entry
, gInfo
->type
);
1440 else if (!sGameObjectDisplayInfoStore
.LookupEntry(gInfo
->displayId
))
1442 sLog
.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.", guid
, entry
, gInfo
->type
, gInfo
->displayId
);
1446 GameObjectData
& data
= mGameObjectDataMap
[guid
];
1449 data
.mapid
= fields
[ 2].GetUInt32();
1450 data
.posX
= fields
[ 3].GetFloat();
1451 data
.posY
= fields
[ 4].GetFloat();
1452 data
.posZ
= fields
[ 5].GetFloat();
1453 data
.orientation
= fields
[ 6].GetFloat();
1454 data
.rotation
.x
= fields
[ 7].GetFloat();
1455 data
.rotation
.y
= fields
[ 8].GetFloat();
1456 data
.rotation
.z
= fields
[ 9].GetFloat();
1457 data
.rotation
.w
= fields
[10].GetFloat();
1458 data
.spawntimesecs
= fields
[11].GetInt32();
1459 data
.animprogress
= fields
[12].GetUInt32();
1460 uint32 go_state
= fields
[13].GetUInt32();
1461 data
.spawnMask
= fields
[14].GetUInt8();
1462 data
.phaseMask
= fields
[15].GetUInt16();
1463 int16 gameEvent
= fields
[16].GetInt16();
1464 int16 GuidPoolId
= fields
[17].GetInt16();
1465 int16 EntryPoolId
= fields
[18].GetInt16();
1467 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(data
.mapid
);
1470 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) that spawned at nonexistent map (Id: %u), skip", guid
, data
.id
, data
.mapid
);
1474 if (data
.spawnMask
& ~spawnMasks
[data
.mapid
])
1475 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u), skip", guid
, data
.id
, data
.spawnMask
, data
.mapid
);
1477 if (data
.spawntimesecs
== 0 && gInfo
->IsDespawnAtAction())
1479 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.", guid
, data
.id
);
1482 if (go_state
>= MAX_GO_STATE
)
1484 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid
, data
.id
, go_state
);
1487 data
.go_state
= GOState(go_state
);
1489 if (data
.rotation
.x
< -1.0f
|| data
.rotation
.x
> 1.0f
)
1491 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.x (%f) value, skip", guid
, data
.id
, data
.rotation
.x
);
1495 if (data
.rotation
.y
< -1.0f
|| data
.rotation
.y
> 1.0f
)
1497 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.y (%f) value, skip", guid
, data
.id
, data
.rotation
.y
);
1501 if (data
.rotation
.z
< -1.0f
|| data
.rotation
.z
> 1.0f
)
1503 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.z (%f) value, skip", guid
, data
.id
, data
.rotation
.z
);
1507 if (data
.rotation
.w
< -1.0f
|| data
.rotation
.w
> 1.0f
)
1509 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation.w (%f) value, skip", guid
, data
.id
, data
.rotation
.w
);
1513 if (!MapManager::IsValidMapCoord(data
.mapid
, data
.posX
, data
.posY
, data
.posZ
, data
.orientation
))
1515 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid
, data
.id
);
1519 if (data
.phaseMask
== 0)
1521 sLog
.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid
, data
.id
);
1525 if (gameEvent
== 0 && GuidPoolId
== 0 && EntryPoolId
== 0) // if not this is to be managed by GameEvent System or Pool system
1526 AddGameobjectToGrid(guid
, &data
);
1531 while (result
->NextRow());
1536 sLog
.outString(">> Loaded " SIZEFMTD
" gameobjects", mGameObjectDataMap
.size());
1539 void ObjectMgr::LoadGameObjectAddon()
1541 sGameObjectDataAddonStorage
.Load();
1543 sLog
.outString(">> Loaded %u gameobject addons", sGameObjectDataAddonStorage
.GetRecordCount());
1546 for (uint32 i
= 1; i
< sGameObjectDataAddonStorage
.GetMaxEntry(); ++i
)
1548 GameObjectDataAddon
const* addon
= sGameObjectDataAddonStorage
.LookupEntry
<GameObjectDataAddon
>(i
);
1552 if (!GetGODataPair(addon
->guid
))
1554 sLog
.outErrorDb("Gameobject (GUID: %u) does not exist but has a record in `gameobject_addon`", addon
->guid
);
1558 if (!addon
->path_rotation
.isUnit())
1560 sLog
.outErrorDb("Gameobject (GUID: %u) has invalid path rotation", addon
->guid
);
1561 const_cast<GameObjectDataAddon
*>(addon
)->path_rotation
= QuaternionData(0.f
, 0.f
, 0.f
, 1.f
);
1566 void ObjectMgr::AddGameobjectToGrid(uint32 guid
, GameObjectData
const* data
)
1568 uint8 mask
= data
->spawnMask
;
1569 for (uint8 i
= 0; mask
!= 0; ++i
, mask
>>= 1)
1573 CellPair cell_pair
= MaNGOS::ComputeCellPair(data
->posX
, data
->posY
);
1574 uint32 cell_id
= (cell_pair
.y_coord
* TOTAL_NUMBER_OF_CELLS_PER_MAP
) + cell_pair
.x_coord
;
1576 CellObjectGuids
& cell_guids
= mMapObjectGuids
[MAKE_PAIR32(data
->mapid
, i
)][cell_id
];
1577 cell_guids
.gameobjects
.insert(guid
);
1582 void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid
, GameObjectData
const* data
)
1584 uint8 mask
= data
->spawnMask
;
1585 for (uint8 i
= 0; mask
!= 0; ++i
, mask
>>= 1)
1589 CellPair cell_pair
= MaNGOS::ComputeCellPair(data
->posX
, data
->posY
);
1590 uint32 cell_id
= (cell_pair
.y_coord
* TOTAL_NUMBER_OF_CELLS_PER_MAP
) + cell_pair
.x_coord
;
1592 CellObjectGuids
& cell_guids
= mMapObjectGuids
[MAKE_PAIR32(data
->mapid
, i
)][cell_id
];
1593 cell_guids
.gameobjects
.erase(guid
);
1598 // name must be checked to correctness (if received) before call this function
1599 ObjectGuid
ObjectMgr::GetPlayerGuidByName(std::string name
) const
1603 CharacterDatabase
.escape_string(name
);
1605 // Player name safe to sending to DB (checked at login) and this function using
1606 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT guid FROM characters WHERE name = '%s'", name
.c_str());
1609 guid
= ObjectGuid(HIGHGUID_PLAYER
, (*result
)[0].GetUInt32());
1617 bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid
, std::string
& name
) const
1619 // prevent DB access for online player
1620 if (Player
* player
= GetPlayer(guid
))
1622 name
= player
->GetName();
1626 uint32 lowguid
= guid
.GetCounter();
1628 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT name FROM characters WHERE guid = '%u'", lowguid
);
1632 name
= (*result
)[0].GetCppString();
1640 Team
ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid
) const
1642 // prevent DB access for online player
1643 if (Player
* player
= GetPlayer(guid
))
1644 return Player::TeamForRace(player
->getRace());
1646 uint32 lowguid
= guid
.GetCounter();
1648 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT race FROM characters WHERE guid = '%u'", lowguid
);
1652 uint8 race
= (*result
)[0].GetUInt8();
1654 return Player::TeamForRace(race
);
1660 uint32
ObjectMgr::GetPlayerAccountIdByGUID(ObjectGuid guid
) const
1662 if (!guid
.IsPlayer())
1665 // prevent DB access for online player
1666 if (Player
* player
= GetPlayer(guid
))
1667 return player
->GetSession()->GetAccountId();
1669 uint32 lowguid
= guid
.GetCounter();
1671 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT account FROM characters WHERE guid = '%u'", lowguid
);
1674 uint32 acc
= (*result
)[0].GetUInt32();
1682 uint32
ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string
& name
) const
1684 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT account FROM characters WHERE name = '%s'", name
.c_str());
1687 uint32 acc
= (*result
)[0].GetUInt32();
1695 void ObjectMgr::LoadItemLocales()
1697 mItemLocaleMap
.clear(); // need for reload case
1699 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,name_loc1,description_loc1,name_loc2,description_loc2,name_loc3,description_loc3,name_loc4,description_loc4,name_loc5,description_loc5,name_loc6,description_loc6,name_loc7,description_loc7,name_loc8,description_loc8 FROM locales_item");
1708 sLog
.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
1712 BarGoLink
bar(result
->GetRowCount());
1716 Field
* fields
= result
->Fetch();
1719 uint32 entry
= fields
[0].GetUInt32();
1721 if (!GetItemPrototype(entry
))
1723 ERROR_DB_STRICT_LOG("Table `locales_item` has data for nonexistent item entry %u, skipped.", entry
);
1727 ItemLocale
& data
= mItemLocaleMap
[entry
];
1729 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
1731 std::string str
= fields
[1 + 2 * (i
- 1)].GetCppString();
1734 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
1737 if ((int32
)data
.Name
.size() <= idx
)
1738 data
.Name
.resize(idx
+ 1);
1740 data
.Name
[idx
] = str
;
1744 str
= fields
[1 + 2 * (i
- 1) + 1].GetCppString();
1747 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
1750 if ((int32
)data
.Description
.size() <= idx
)
1751 data
.Description
.resize(idx
+ 1);
1753 data
.Description
[idx
] = str
;
1758 while (result
->NextRow());
1763 sLog
.outString(">> Loaded " SIZEFMTD
" Item locale strings", mItemLocaleMap
.size());
1766 struct SQLItemLoader
: public SQLStorageLoaderBase
<SQLItemLoader
, SQLStorage
>
1769 void convert_from_str(uint32
/*field_pos*/, char const* src
, D
& dst
)
1771 dst
= D(sScriptMgr
.GetScriptId(src
));
1775 void ObjectMgr::LoadItemPrototypes()
1777 SQLItemLoader loader
;
1778 loader
.Load(sItemStorage
);
1779 sLog
.outString(">> Loaded %u item prototypes", sItemStorage
.GetRecordCount());
1782 // check data correctness
1783 for (uint32 i
= 1; i
< sItemStorage
.GetMaxEntry(); ++i
)
1785 ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
);
1786 ItemEntry
const *dbcitem
= sItemStore
.LookupEntry(i
);
1789 /* to many errors, and possible not all items really used in game
1791 sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
1798 if(proto
->Class
!= dbcitem
->Class
)
1800 sLog
.outErrorDb("Item (Entry: %u) not correct class %u, must be %u (still using DB value).",i
,proto
->Class
,dbcitem
->Class
);
1801 // It safe let use Class from DB
1803 /* disabled: have some strange wrong cases for Subclass values.
1804 for enable also uncomment Subclass field in ItemEntry structure and in Itemfmt[]
1805 if(proto->SubClass != dbcitem->SubClass)
1807 sLog.outErrorDb("Item (Entry: %u) not correct (Class: %u, Sub: %u) pair, must be (Class: %u, Sub: %u) (still using DB value).",i,proto->Class,proto->SubClass,dbcitem->Class,dbcitem->SubClass);
1808 // It safe let use Subclass from DB
1812 if(proto
->Unk0
!= dbcitem
->Unk0
)
1814 sLog
.outErrorDb("Item (Entry: %u) not correct %i Unk0, must be %i (still using DB value).",i
,proto
->Unk0
,dbcitem
->Unk0
);
1815 // It safe let use Unk0 from DB
1818 if(proto
->Material
!= dbcitem
->Material
)
1820 sLog
.outErrorDb("Item (Entry: %u) not correct %i material, must be %i (still using DB value).",i
,proto
->Material
,dbcitem
->Material
);
1821 // It safe let use Material from DB
1824 if(proto
->InventoryType
!= dbcitem
->InventoryType
)
1826 sLog
.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i
,proto
->InventoryType
,dbcitem
->InventoryType
);
1827 // It safe let use InventoryType from DB
1830 if(proto
->DisplayInfoID
!= dbcitem
->DisplayId
)
1832 sLog
.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i
,proto
->DisplayInfoID
,dbcitem
->DisplayId
);
1833 const_cast<ItemPrototype
*>(proto
)->DisplayInfoID
= dbcitem
->DisplayId
;
1835 if(proto
->Sheath
!= dbcitem
->Sheath
)
1837 sLog
.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).",i
,proto
->Sheath
,dbcitem
->Sheath
);
1838 const_cast<ItemPrototype
*>(proto
)->Sheath
= dbcitem
->Sheath
;
1843 sLog
.outErrorDb("Item (Entry: %u) not correct (not listed in list of existing items).", i
);
1846 if (proto
->Class
>= MAX_ITEM_CLASS
)
1848 sLog
.outErrorDb("Item (Entry: %u) has wrong Class value (%u)", i
, proto
->Class
);
1849 const_cast<ItemPrototype
*>(proto
)->Class
= ITEM_CLASS_MISC
;
1852 if (proto
->SubClass
>= MaxItemSubclassValues
[proto
->Class
])
1854 sLog
.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u", i
, proto
->SubClass
, proto
->Class
);
1855 const_cast<ItemPrototype
*>(proto
)->SubClass
= 0;// exist for all item classes
1858 if (proto
->Quality
>= MAX_ITEM_QUALITY
)
1860 sLog
.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)", i
, proto
->Quality
);
1861 const_cast<ItemPrototype
*>(proto
)->Quality
= ITEM_QUALITY_NORMAL
;
1864 if (proto
->Flags2
& ITEM_FLAG2_HORDE_ONLY
)
1866 if (FactionEntry
const* faction
= sFactionStore
.LookupEntry(HORDE
))
1867 if ((proto
->AllowableRace
& faction
->BaseRepRaceMask
[0]) == 0)
1868 sLog
.outErrorDb("Item (Entry: %u) have in `AllowableRace` races (%u) only not compatible with ITEM_FLAG2_HORDE_ONLY (%u) in Flags field, item any way will can't be equipped or use by this races.",
1869 i
, proto
->AllowableRace
, ITEM_FLAG2_HORDE_ONLY
);
1871 if (proto
->Flags2
& ITEM_FLAG2_ALLIANCE_ONLY
)
1872 sLog
.outErrorDb("Item (Entry: %u) have in `Flags2` flags ITEM_FLAG2_ALLIANCE_ONLY (%u) and ITEM_FLAG2_HORDE_ONLY (%u) in Flags field, this is wrong combination.",
1873 i
, ITEM_FLAG2_ALLIANCE_ONLY
, ITEM_FLAG2_HORDE_ONLY
);
1875 else if (proto
->Flags2
& ITEM_FLAG2_ALLIANCE_ONLY
)
1877 if (FactionEntry
const* faction
= sFactionStore
.LookupEntry(ALLIANCE
))
1878 if ((proto
->AllowableRace
& faction
->BaseRepRaceMask
[0]) == 0)
1879 sLog
.outErrorDb("Item (Entry: %u) have in `AllowableRace` races (%u) only not compatible with ITEM_FLAG2_ALLIANCE_ONLY (%u) in Flags field, item any way will can't be equipped or use by this races.",
1880 i
, proto
->AllowableRace
, ITEM_FLAG2_ALLIANCE_ONLY
);
1883 if (proto
->BuyCount
<= 0)
1885 sLog
.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", i
, proto
->BuyCount
);
1886 const_cast<ItemPrototype
*>(proto
)->BuyCount
= 1;
1889 if (proto
->InventoryType
>= MAX_INVTYPE
)
1891 sLog
.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)", i
, proto
->InventoryType
);
1892 const_cast<ItemPrototype
*>(proto
)->InventoryType
= INVTYPE_NON_EQUIP
;
1895 if (proto
->InventoryType
!= INVTYPE_NON_EQUIP
)
1897 if (proto
->Flags
& ITEM_FLAG_LOOTABLE
)
1899 sLog
.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_LOOTABLE (%u), flag removed.", i
, ITEM_FLAG_LOOTABLE
);
1900 const_cast<ItemPrototype
*>(proto
)->Flags
&= ~ITEM_FLAG_LOOTABLE
;
1903 if (proto
->Flags
& ITEM_FLAG_MILLABLE
)
1905 sLog
.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_MILLABLE (%u), flag removed.", i
, ITEM_FLAG_MILLABLE
);
1906 const_cast<ItemPrototype
*>(proto
)->Flags
&= ~ITEM_FLAG_MILLABLE
;
1909 if (proto
->Flags
& ITEM_FLAG_PROSPECTABLE
)
1911 sLog
.outErrorDb("Item container (Entry: %u) has not allowed for containers flag ITEM_FLAG_PROSPECTABLE (%u), flag removed.", i
, ITEM_FLAG_PROSPECTABLE
);
1912 const_cast<ItemPrototype
*>(proto
)->Flags
&= ~ITEM_FLAG_PROSPECTABLE
;
1915 else if (proto
->InventoryType
!= INVTYPE_BAG
)
1917 if (proto
->ContainerSlots
> 0)
1919 sLog
.outErrorDb("Non-container item (Entry: %u) has ContainerSlots (%u), set to 0.", i
, proto
->ContainerSlots
);
1920 const_cast<ItemPrototype
*>(proto
)->ContainerSlots
= 0;
1924 if (proto
->RequiredSkill
>= MAX_SKILL_TYPE
)
1926 sLog
.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)", i
, proto
->RequiredSkill
);
1927 const_cast<ItemPrototype
*>(proto
)->RequiredSkill
= 0;
1931 // can be used in equip slot, as page read use in inventory, or spell casting at use
1932 bool req
= proto
->InventoryType
!= INVTYPE_NON_EQUIP
|| proto
->PageText
;
1935 for (int j
= 0; j
< MAX_ITEM_PROTO_SPELLS
; ++j
)
1937 if (proto
->Spells
[j
].SpellId
)
1947 if (!(proto
->AllowableClass
& CLASSMASK_ALL_PLAYABLE
))
1948 sLog
.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.", i
, proto
->AllowableClass
);
1950 if (!(proto
->AllowableRace
& RACEMASK_ALL_PLAYABLE
))
1951 sLog
.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.", i
, proto
->AllowableRace
);
1955 if (proto
->RequiredSpell
&& !sSpellStore
.LookupEntry(proto
->RequiredSpell
))
1957 sLog
.outErrorDb("Item (Entry: %u) have wrong (nonexistent) spell in RequiredSpell (%u)", i
, proto
->RequiredSpell
);
1958 const_cast<ItemPrototype
*>(proto
)->RequiredSpell
= 0;
1961 if (proto
->RequiredReputationRank
>= MAX_REPUTATION_RANK
)
1962 sLog
.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", i
, proto
->RequiredReputationRank
);
1964 if (proto
->RequiredReputationFaction
)
1966 if (!sFactionStore
.LookupEntry(proto
->RequiredReputationFaction
))
1968 sLog
.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)", i
, proto
->RequiredReputationFaction
);
1969 const_cast<ItemPrototype
*>(proto
)->RequiredReputationFaction
= 0;
1972 if (proto
->RequiredReputationRank
== MIN_REPUTATION_RANK
)
1973 sLog
.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", i
);
1975 else if (proto
->RequiredReputationRank
> MIN_REPUTATION_RANK
)
1976 sLog
.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.", i
);
1978 if (proto
->MaxCount
< -1)
1980 sLog
.outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.", i
, proto
->MaxCount
);
1981 const_cast<ItemPrototype
*>(proto
)->MaxCount
= -1;
1984 if (proto
->Stackable
== 0)
1986 sLog
.outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.", i
, proto
->Stackable
);
1987 const_cast<ItemPrototype
*>(proto
)->Stackable
= 1;
1989 else if (proto
->Stackable
< -1)
1991 sLog
.outErrorDb("Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.", i
, proto
->Stackable
);
1992 const_cast<ItemPrototype
*>(proto
)->Stackable
= -1;
1994 else if (proto
->Stackable
> 1000)
1996 sLog
.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (1000).", i
, proto
->Stackable
);
1997 const_cast<ItemPrototype
*>(proto
)->Stackable
= 1000;
2000 if (proto
->ContainerSlots
)
2002 if (proto
->ContainerSlots
> MAX_BAG_SIZE
)
2004 sLog
.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", i
, proto
->ContainerSlots
, MAX_BAG_SIZE
);
2005 const_cast<ItemPrototype
*>(proto
)->ContainerSlots
= MAX_BAG_SIZE
;
2009 for (int j
= 0; j
< MAX_ITEM_PROTO_STATS
; ++j
)
2011 // for ItemStatValue != 0
2012 if (proto
->ItemStat
[j
].ItemStatValue
&& proto
->ItemStat
[j
].ItemStatType
>= MAX_ITEM_MOD
)
2014 sLog
.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)", i
, j
+ 1, proto
->ItemStat
[j
].ItemStatType
);
2015 const_cast<ItemPrototype
*>(proto
)->ItemStat
[j
].ItemStatType
= 0;
2018 switch (proto
->ItemStat
[j
].ItemStatType
)
2020 case ITEM_MOD_HEALTH
:
2021 case ITEM_MOD_DEFENSE_SKILL_RATING
:
2022 case ITEM_MOD_BLOCK_RATING
:
2023 case ITEM_MOD_HIT_MELEE_RATING
:
2024 case ITEM_MOD_HIT_RANGED_RATING
:
2025 case ITEM_MOD_HIT_SPELL_RATING
:
2026 case ITEM_MOD_CRIT_MELEE_RATING
:
2027 case ITEM_MOD_CRIT_SPELL_RATING
:
2028 case ITEM_MOD_HIT_TAKEN_MELEE_RATING
:
2029 case ITEM_MOD_HIT_TAKEN_RANGED_RATING
:
2030 case ITEM_MOD_HIT_TAKEN_SPELL_RATING
:
2031 case ITEM_MOD_CRIT_TAKEN_MELEE_RATING
:
2032 case ITEM_MOD_CRIT_TAKEN_RANGED_RATING
:
2033 case ITEM_MOD_CRIT_TAKEN_SPELL_RATING
:
2034 case ITEM_MOD_HASTE_MELEE_RATING
:
2035 case ITEM_MOD_HASTE_RANGED_RATING
:
2036 case ITEM_MOD_HASTE_SPELL_RATING
:
2037 case ITEM_MOD_HIT_TAKEN_RATING
:
2038 case ITEM_MOD_CRIT_TAKEN_RATING
:
2039 case ITEM_MOD_FERAL_ATTACK_POWER
:
2040 case ITEM_MOD_SPELL_HEALING_DONE
:
2041 case ITEM_MOD_SPELL_DAMAGE_DONE
:
2042 case ITEM_MOD_MANA_REGENERATION
:
2043 case ITEM_MOD_ARMOR_PENETRATION_RATING
:
2044 case ITEM_MOD_BLOCK_VALUE
:
2045 sLog
.outErrorDb("Item (Entry: %u) has deprecated stat_type%d (%u)", i
, j
+ 1, proto
->ItemStat
[j
].ItemStatType
);
2052 if (proto
->DamageType
>= MAX_SPELL_SCHOOL
)
2054 sLog
.outErrorDb("Item (Entry: %u) has wrong damagetype (%u)", i
, proto
->DamageType
);
2055 const_cast<ItemPrototype
*>(proto
)->DamageType
= 0;
2059 if ((proto
->Spells
[0].SpellId
== SPELL_ID_GENERIC_LEARN
) || (proto
->Spells
[0].SpellId
== SPELL_ID_GENERIC_LEARN_PET
))
2062 if (proto
->Spells
[0].SpellTrigger
!= ITEM_SPELLTRIGGER_ON_USE
)
2064 sLog
.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format", i
, 0 + 1, proto
->Spells
[0].SpellTrigger
);
2065 const_cast<ItemPrototype
*>(proto
)->Spells
[0].SpellId
= 0;
2066 const_cast<ItemPrototype
*>(proto
)->Spells
[0].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2067 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellId
= 0;
2068 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2071 // spell_2 have learning spell
2072 if (proto
->Spells
[1].SpellTrigger
!= ITEM_SPELLTRIGGER_LEARN_SPELL_ID
)
2074 sLog
.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u) for special learning format.", i
, 1 + 1, proto
->Spells
[1].SpellTrigger
);
2075 const_cast<ItemPrototype
*>(proto
)->Spells
[0].SpellId
= 0;
2076 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellId
= 0;
2077 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2079 else if (!proto
->Spells
[1].SpellId
)
2081 sLog
.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.", i
, 1 + 1);
2082 const_cast<ItemPrototype
*>(proto
)->Spells
[0].SpellId
= 0;
2083 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2087 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(proto
->Spells
[1].SpellId
);
2090 sLog
.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)", i
, 1 + 1, proto
->Spells
[1].SpellId
);
2091 const_cast<ItemPrototype
*>(proto
)->Spells
[0].SpellId
= 0;
2092 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellId
= 0;
2093 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2095 // allowed only in special format
2096 else if ((proto
->Spells
[1].SpellId
== SPELL_ID_GENERIC_LEARN
) || (proto
->Spells
[1].SpellId
== SPELL_ID_GENERIC_LEARN_PET
))
2098 sLog
.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)", i
, 1 + 1, proto
->Spells
[1].SpellId
);
2099 const_cast<ItemPrototype
*>(proto
)->Spells
[0].SpellId
= 0;
2100 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellId
= 0;
2101 const_cast<ItemPrototype
*>(proto
)->Spells
[1].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2105 // spell_3*,spell_4*,spell_5* is empty
2106 for (int j
= 2; j
< MAX_ITEM_PROTO_SPELLS
; ++j
)
2108 if (proto
->Spells
[j
].SpellTrigger
!= ITEM_SPELLTRIGGER_ON_USE
)
2110 sLog
.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i
, j
+ 1, proto
->Spells
[j
].SpellTrigger
);
2111 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellId
= 0;
2112 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2114 else if (proto
->Spells
[j
].SpellId
!= 0)
2116 sLog
.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format", i
, j
+ 1, proto
->Spells
[j
].SpellId
);
2117 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellId
= 0;
2121 // normal spell list
2124 for (int j
= 0; j
< MAX_ITEM_PROTO_SPELLS
; ++j
)
2126 if (proto
->Spells
[j
].SpellTrigger
>= MAX_ITEM_SPELLTRIGGER
|| proto
->Spells
[j
].SpellTrigger
== ITEM_SPELLTRIGGER_LEARN_SPELL_ID
)
2128 sLog
.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i
, j
+ 1, proto
->Spells
[j
].SpellTrigger
);
2129 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellId
= 0;
2130 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellTrigger
= ITEM_SPELLTRIGGER_ON_USE
;
2132 // on hit can be sued only at weapon
2133 else if (proto
->Spells
[j
].SpellTrigger
== ITEM_SPELLTRIGGER_CHANCE_ON_HIT
)
2135 if (proto
->Class
!= ITEM_CLASS_WEAPON
)
2136 sLog
.outErrorDb("Item (Entry: %u) isn't weapon (Class: %u) but has on hit spelltrigger_%d (%u), it will not triggered.", i
, proto
->Class
, j
+ 1, proto
->Spells
[j
].SpellTrigger
);
2139 if (proto
->Spells
[j
].SpellId
)
2141 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(proto
->Spells
[j
].SpellId
);
2144 sLog
.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)", i
, j
+ 1, proto
->Spells
[j
].SpellId
);
2145 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellId
= 0;
2147 // allowed only in special format
2148 else if ((proto
->Spells
[j
].SpellId
== SPELL_ID_GENERIC_LEARN
) || (proto
->Spells
[j
].SpellId
== SPELL_ID_GENERIC_LEARN_PET
))
2150 sLog
.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)", i
, j
+ 1, proto
->Spells
[j
].SpellId
);
2151 const_cast<ItemPrototype
*>(proto
)->Spells
[j
].SpellId
= 0;
2157 if (proto
->Bonding
>= MAX_BIND_TYPE
)
2158 sLog
.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)", i
, proto
->Bonding
);
2160 if (proto
->PageText
)
2162 if (!sPageTextStore
.LookupEntry
<PageText
>(proto
->PageText
))
2163 sLog
.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i
, proto
->PageText
);
2166 if (proto
->LockID
&& !sLockStore
.LookupEntry(proto
->LockID
))
2167 sLog
.outErrorDb("Item (Entry: %u) has wrong LockID (%u)", i
, proto
->LockID
);
2169 if (proto
->Sheath
>= MAX_SHEATHETYPE
)
2171 sLog
.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)", i
, proto
->Sheath
);
2172 const_cast<ItemPrototype
*>(proto
)->Sheath
= SHEATHETYPE_NONE
;
2175 if (proto
->RandomProperty
&& !sItemRandomPropertiesStore
.LookupEntry(GetItemEnchantMod(proto
->RandomProperty
)))
2177 sLog
.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)", i
, proto
->RandomProperty
);
2178 const_cast<ItemPrototype
*>(proto
)->RandomProperty
= 0;
2181 if (proto
->RandomSuffix
&& !sItemRandomSuffixStore
.LookupEntry(GetItemEnchantMod(proto
->RandomSuffix
)))
2183 sLog
.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)", i
, proto
->RandomSuffix
);
2184 const_cast<ItemPrototype
*>(proto
)->RandomSuffix
= 0;
2187 // item can have not null only one from field values
2188 if (proto
->RandomProperty
&& proto
->RandomSuffix
)
2190 sLog
.outErrorDb("Item (Entry: %u) have RandomProperty==%u and RandomSuffix==%u, but must have one from field = 0",
2191 proto
->ItemId
, proto
->RandomProperty
, proto
->RandomSuffix
);
2192 const_cast<ItemPrototype
*>(proto
)->RandomSuffix
= 0;
2195 if (proto
->ItemSet
&& !sItemSetStore
.LookupEntry(proto
->ItemSet
))
2197 sLog
.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)", i
, proto
->ItemSet
);
2198 const_cast<ItemPrototype
*>(proto
)->ItemSet
= 0;
2201 if (proto
->Area
&& !GetAreaEntryByAreaID(proto
->Area
))
2202 sLog
.outErrorDb("Item (Entry: %u) has wrong Area (%u)", i
, proto
->Area
);
2204 if (proto
->Map
&& !sMapStore
.LookupEntry(proto
->Map
))
2205 sLog
.outErrorDb("Item (Entry: %u) has wrong Map (%u)", i
, proto
->Map
);
2207 if (proto
->BagFamily
)
2210 for (uint32 j
= 0; j
< sizeof(proto
->BagFamily
) * 8; ++j
)
2212 uint32 mask
= 1 << j
;
2213 if (!(proto
->BagFamily
& mask
))
2216 ItemBagFamilyEntry
const* bf
= sItemBagFamilyStore
.LookupEntry(j
+ 1);
2219 sLog
.outErrorDb("Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", i
);
2220 const_cast<ItemPrototype
*>(proto
)->BagFamily
&= ~mask
;
2224 /*if(BAG_FAMILY_MASK_CURRENCY_TOKENS & mask)
2226 CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(proto->ItemId);
2229 sLog.outErrorDb("Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit",i);
2230 const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
2236 if (proto
->TotemCategory
&& !sTotemCategoryStore
.LookupEntry(proto
->TotemCategory
))
2237 sLog
.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)", i
, proto
->TotemCategory
);
2239 for (int j
= 0; j
< MAX_ITEM_PROTO_SOCKETS
; ++j
)
2241 if (proto
->Socket
[j
].Color
&& (proto
->Socket
[j
].Color
& SOCKET_COLOR_ALL
) != proto
->Socket
[j
].Color
)
2243 sLog
.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)", i
, j
+ 1, proto
->Socket
[j
].Color
);
2244 const_cast<ItemPrototype
*>(proto
)->Socket
[j
].Color
= 0;
2248 if (proto
->GemProperties
&& !sGemPropertiesStore
.LookupEntry(proto
->GemProperties
))
2249 sLog
.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)", i
, proto
->GemProperties
);
2251 if (proto
->RequiredDisenchantSkill
< -1)
2253 sLog
.outErrorDb("Item (Entry: %u) has wrong RequiredDisenchantSkill (%i), set to (-1).", i
, proto
->RequiredDisenchantSkill
);
2254 const_cast<ItemPrototype
*>(proto
)->RequiredDisenchantSkill
= -1;
2256 else if (proto
->RequiredDisenchantSkill
!= -1)
2258 if (proto
->Quality
> ITEM_QUALITY_EPIC
|| proto
->Quality
< ITEM_QUALITY_UNCOMMON
)
2260 ERROR_DB_STRICT_LOG("Item (Entry: %u) has unexpected RequiredDisenchantSkill (%u) for non-disenchantable quality (%u), reset it.",
2261 i
, proto
->RequiredDisenchantSkill
, proto
->Quality
);
2262 const_cast<ItemPrototype
*>(proto
)->RequiredDisenchantSkill
= -1;
2264 else if (proto
->Class
!= ITEM_CLASS_WEAPON
&& proto
->Class
!= ITEM_CLASS_ARMOR
)
2266 // some wrong data in wdb for unused items
2267 ERROR_DB_STRICT_LOG("Item (Entry: %u) has unexpected RequiredDisenchantSkill (%u) for non-disenchantable item class (%u), reset it.",
2268 i
, proto
->RequiredDisenchantSkill
, proto
->Class
);
2269 const_cast<ItemPrototype
*>(proto
)->RequiredDisenchantSkill
= -1;
2273 if (proto
->DisenchantID
)
2275 if (proto
->Quality
> ITEM_QUALITY_EPIC
|| proto
->Quality
< ITEM_QUALITY_UNCOMMON
)
2277 sLog
.outErrorDb("Item (Entry: %u) has wrong quality (%u) for disenchanting, remove disenchanting loot id.", i
, proto
->Quality
);
2278 const_cast<ItemPrototype
*>(proto
)->DisenchantID
= 0;
2280 else if (proto
->Class
!= ITEM_CLASS_WEAPON
&& proto
->Class
!= ITEM_CLASS_ARMOR
)
2282 sLog
.outErrorDb("Item (Entry: %u) has wrong item class (%u) for disenchanting, remove disenchanting loot id.", i
, proto
->Class
);
2283 const_cast<ItemPrototype
*>(proto
)->DisenchantID
= 0;
2285 else if (proto
->RequiredDisenchantSkill
< 0)
2287 sLog
.outErrorDb("Item (Entry: %u) marked as non-disenchantable by RequiredDisenchantSkill == -1, remove disenchanting loot id.", i
);
2288 const_cast<ItemPrototype
*>(proto
)->DisenchantID
= 0;
2294 if (proto
->RequiredDisenchantSkill
>= 0)
2295 ERROR_DB_STRICT_LOG("Item (Entry: %u) marked as disenchantable by RequiredDisenchantSkill, but not have disenchanting loot id.", i
);
2298 if (proto
->FoodType
>= MAX_PET_DIET
)
2300 sLog
.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)", i
, proto
->FoodType
);
2301 const_cast<ItemPrototype
*>(proto
)->FoodType
= 0;
2304 if (proto
->ItemLimitCategory
&& !sItemLimitCategoryStore
.LookupEntry(proto
->ItemLimitCategory
))
2306 sLog
.outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)", i
, proto
->ItemLimitCategory
);
2307 const_cast<ItemPrototype
*>(proto
)->ItemLimitCategory
= 0;
2310 if (proto
->HolidayId
&& !sHolidaysStore
.LookupEntry(proto
->HolidayId
))
2312 sLog
.outErrorDb("Item (Entry: %u) has wrong HolidayId value (%u)", i
, proto
->HolidayId
);
2313 const_cast<ItemPrototype
*>(proto
)->HolidayId
= 0;
2316 if (proto
->ExtraFlags
)
2318 if (proto
->ExtraFlags
& ~ITEM_EXTRA_ALL
)
2319 sLog
.outErrorDb("Item (Entry: %u) has wrong ExtraFlags (%u) with unused bits set", i
, proto
->ExtraFlags
);
2321 if (proto
->ExtraFlags
& ITEM_EXTRA_NON_CONSUMABLE
)
2323 bool can_be_need
= false;
2324 for (int j
= 0; j
< MAX_ITEM_PROTO_SPELLS
; ++j
)
2326 if (proto
->Spells
[j
].SpellCharges
< 0)
2335 sLog
.outErrorDb("Item (Entry: %u) has redundant non-consumable flag in ExtraFlags, item not have negative charges", i
);
2336 const_cast<ItemPrototype
*>(proto
)->ExtraFlags
&= ~ITEM_EXTRA_NON_CONSUMABLE
;
2340 if (proto
->ExtraFlags
& ITEM_EXTRA_REAL_TIME_DURATION
)
2342 if (proto
->Duration
== 0)
2344 sLog
.outErrorDb("Item (Entry: %u) has redundant real-time duration flag in ExtraFlags, item not have duration", i
);
2345 const_cast<ItemPrototype
*>(proto
)->ExtraFlags
&= ~ITEM_EXTRA_REAL_TIME_DURATION
;
2351 // check some dbc referenced items (avoid duplicate reports)
2352 std::set
<uint32
> notFoundOutfit
;
2353 for (uint32 i
= 1; i
< sCharStartOutfitStore
.GetNumRows(); ++i
)
2355 CharStartOutfitEntry
const* entry
= sCharStartOutfitStore
.LookupEntry(i
);
2359 for (int j
= 0; j
< MAX_OUTFIT_ITEMS
; ++j
)
2361 if (entry
->ItemId
[j
] <= 0)
2364 uint32 item_id
= entry
->ItemId
[j
];
2366 if (!GetItemPrototype(item_id
))
2367 if (item_id
!= 40582) // nonexistent item by default but referenced in DBC, skip it from errors
2368 notFoundOutfit
.insert(item_id
);
2372 for (std::set
<uint32
>::const_iterator itr
= notFoundOutfit
.begin(); itr
!= notFoundOutfit
.end(); ++itr
)
2373 sLog
.outErrorDb("Item (Entry: %u) not exist in `item_template` but referenced in `CharStartOutfit.dbc`", *itr
);
2376 void ObjectMgr::LoadItemConverts()
2378 m_ItemConvert
.clear(); // needed for reload case
2382 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,item FROM item_convert");
2391 sLog
.outErrorDb(">> Loaded 0 Item converts . DB table `item_convert` is empty.");
2395 BarGoLink
bar(result
->GetRowCount());
2399 Field
* fields
= result
->Fetch();
2402 uint32 itemEntry
= fields
[0].GetUInt32();
2403 uint32 itemTargetId
= fields
[1].GetUInt32();
2405 ItemPrototype
const* pItemEntryProto
= sItemStorage
.LookupEntry
<ItemPrototype
>(itemEntry
);
2406 if (!pItemEntryProto
)
2408 sLog
.outErrorDb("Table `item_convert`: Item %u not exist in `item_template`.", itemEntry
);
2412 ItemPrototype
const* pItemTargetProto
= sItemStorage
.LookupEntry
<ItemPrototype
>(itemTargetId
);
2413 if (!pItemTargetProto
)
2415 sLog
.outErrorDb("Table `item_convert`: Item target %u for original item %u not exist in `item_template`.", itemTargetId
, itemEntry
);
2419 // 2 cases when item convert used
2420 // Boa item with reputation requirement
2421 if ((!(pItemEntryProto
->Flags
& ITEM_FLAG_BOA
) || !pItemEntryProto
->RequiredReputationFaction
) &&
2422 // convertion to another team/race
2423 (pItemTargetProto
->AllowableRace
& pItemEntryProto
->AllowableRace
))
2425 sLog
.outErrorDb("Table `item_convert` not appropriate item %u conversion to %u. Table can be used for BoA items requirement drop or for conversion to another race/team use.", itemEntry
, itemTargetId
);
2429 m_ItemConvert
[itemEntry
] = itemTargetId
;
2433 while (result
->NextRow());
2438 sLog
.outString(">> Loaded %u Item converts", count
);
2441 void ObjectMgr::LoadItemExpireConverts()
2443 m_ItemExpireConvert
.clear(); // needed for reload case
2447 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,item FROM item_expire_convert");
2456 sLog
.outErrorDb(">> Loaded 0 Item expire converts . DB table `item_expire_convert` is empty.");
2460 BarGoLink
bar(result
->GetRowCount());
2464 Field
* fields
= result
->Fetch();
2467 uint32 itemEntry
= fields
[0].GetUInt32();
2468 uint32 itemTargetId
= fields
[1].GetUInt32();
2470 ItemPrototype
const* pItemEntryProto
= sItemStorage
.LookupEntry
<ItemPrototype
>(itemEntry
);
2471 if (!pItemEntryProto
)
2473 sLog
.outErrorDb("Table `item_expire_convert`: Item %u not exist in `item_template`.", itemEntry
);
2477 ItemPrototype
const* pItemTargetProto
= sItemStorage
.LookupEntry
<ItemPrototype
>(itemTargetId
);
2478 if (!pItemTargetProto
)
2480 sLog
.outErrorDb("Table `item_expire_convert`: Item target %u for original item %u not exist in `item_template`.", itemTargetId
, itemEntry
);
2484 // Expire convert possible only for items with duration
2485 if (pItemEntryProto
->Duration
== 0)
2487 sLog
.outErrorDb("Table `item_expire_convert` not appropriate item %u conversion to %u. Table can be used for items with duration.", itemEntry
, itemTargetId
);
2491 m_ItemExpireConvert
[itemEntry
] = itemTargetId
;
2495 while (result
->NextRow());
2500 sLog
.outString(">> Loaded %u Item expire converts", count
);
2503 void ObjectMgr::LoadItemRequiredTarget()
2505 m_ItemRequiredTarget
.clear(); // needed for reload case
2509 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,type,targetEntry FROM item_required_target");
2518 sLog
.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty.");
2522 BarGoLink
bar(result
->GetRowCount());
2526 Field
* fields
= result
->Fetch();
2529 uint32 uiItemId
= fields
[0].GetUInt32();
2530 uint32 uiType
= fields
[1].GetUInt32();
2531 uint32 uiTargetEntry
= fields
[2].GetUInt32();
2533 ItemPrototype
const* pItemProto
= sItemStorage
.LookupEntry
<ItemPrototype
>(uiItemId
);
2537 sLog
.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.", uiItemId
, uiTargetEntry
);
2541 bool bIsItemSpellValid
= false;
2543 for (int i
= 0; i
< MAX_ITEM_PROTO_SPELLS
; ++i
)
2545 if (SpellEntry
const* pSpellInfo
= sSpellStore
.LookupEntry(pItemProto
->Spells
[i
].SpellId
))
2547 if (pItemProto
->Spells
[i
].SpellTrigger
== ITEM_SPELLTRIGGER_ON_USE
)
2549 SpellScriptTargetBounds bounds
= sSpellMgr
.GetSpellScriptTargetBounds(pSpellInfo
->Id
);
2550 if (bounds
.first
!= bounds
.second
)
2553 for (int j
= 0; j
< MAX_EFFECT_INDEX
; ++j
)
2555 SpellEffectEntry
const* spellEffect
= pSpellInfo
->GetSpellEffect(SpellEffectIndex(j
));
2559 if (spellEffect
->EffectImplicitTargetA
== TARGET_CHAIN_DAMAGE
||
2560 spellEffect
->EffectImplicitTargetB
== TARGET_CHAIN_DAMAGE
||
2561 spellEffect
->EffectImplicitTargetA
== TARGET_DUELVSPLAYER
||
2562 spellEffect
->EffectImplicitTargetB
== TARGET_DUELVSPLAYER
)
2564 bIsItemSpellValid
= true;
2568 if (bIsItemSpellValid
)
2574 if (!bIsItemSpellValid
)
2576 sLog
.outErrorDb("Table `item_required_target`: Spell used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in `spell_script_target` or doesn't have item spelltrigger.", uiItemId
);
2580 if (!uiType
|| uiType
> MAX_ITEM_REQ_TARGET_TYPE
)
2582 sLog
.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.", uiType
, uiTargetEntry
);
2588 sLog
.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).", uiType
);
2592 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(uiTargetEntry
))
2594 sLog
.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.", uiTargetEntry
);
2598 m_ItemRequiredTarget
.insert(ItemRequiredTargetMap::value_type(uiItemId
, ItemRequiredTarget(ItemRequiredTargetType(uiType
), uiTargetEntry
)));
2602 while (result
->NextRow());
2607 sLog
.outString(">> Loaded %u Item required targets", count
);
2610 void ObjectMgr::LoadPetLevelInfo()
2612 // Loading levels data
2614 // 0 1 2 3 4 5 6 7 8 9
2615 QueryResult
* result
= WorldDatabase
.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
2625 sLog
.outString(">> Loaded %u level pet stats definitions", count
);
2626 sLog
.outErrorDb("Error loading `pet_levelstats` table or empty table.");
2630 BarGoLink
bar(result
->GetRowCount());
2634 Field
* fields
= result
->Fetch();
2636 uint32 creature_id
= fields
[0].GetUInt32();
2637 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(creature_id
))
2639 sLog
.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id
);
2643 uint32 current_level
= fields
[1].GetUInt32();
2644 if (current_level
> sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
2646 if (current_level
> STRONG_MAX_LEVEL
) // hardcoded level maximum
2647 sLog
.outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL
, current_level
);
2650 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.", current_level
);
2651 ++count
; // make result loading percent "expected" correct in case disabled detail mode for example.
2655 else if (current_level
< 1)
2657 sLog
.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level
);
2661 PetLevelInfo
*& pInfoMapEntry
= petInfo
[creature_id
];
2663 if (pInfoMapEntry
== NULL
)
2664 pInfoMapEntry
= new PetLevelInfo
[sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
)];
2666 // data for level 1 stored in [0] array element, ...
2667 PetLevelInfo
* pLevelInfo
= &pInfoMapEntry
[current_level
- 1];
2669 pLevelInfo
->health
= fields
[2].GetUInt16();
2670 pLevelInfo
->mana
= fields
[3].GetUInt16();
2671 pLevelInfo
->armor
= fields
[9].GetUInt16();
2673 for (int i
= 0; i
< MAX_STATS
; ++i
)
2675 pLevelInfo
->stats
[i
] = fields
[i
+ 4].GetUInt16();
2681 while (result
->NextRow());
2686 sLog
.outString(">> Loaded %u level pet stats definitions", count
);
2689 // Fill gaps and check integrity
2690 for (PetLevelInfoMap::iterator itr
= petInfo
.begin(); itr
!= petInfo
.end(); ++itr
)
2692 PetLevelInfo
* pInfo
= itr
->second
;
2694 // fatal error if no level 1 data
2695 if (!pInfo
|| pInfo
[0].health
== 0)
2697 sLog
.outErrorDb("Creature %u does not have pet stats data for Level 1!", itr
->first
);
2698 Log::WaitBeforeContinueIfNeed();
2703 for (uint32 level
= 1; level
< sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
); ++level
)
2705 if (pInfo
[level
].health
== 0)
2707 sLog
.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr
->first
, level
+ 1, level
);
2708 pInfo
[level
] = pInfo
[level
- 1];
2714 PetLevelInfo
const* ObjectMgr::GetPetLevelInfo(uint32 creature_id
, uint32 level
) const
2716 if (level
> sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
2717 level
= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
);
2719 PetLevelInfoMap::const_iterator itr
= petInfo
.find(creature_id
);
2720 if (itr
== petInfo
.end())
2723 return &itr
->second
[level
- 1]; // data for level 1 stored in [0] array element, ...
2726 void ObjectMgr::LoadPlayerInfo()
2728 // Load playercreate
2730 // 0 1 2 3 4 5 6 7 8
2731 QueryResult
* result
= WorldDatabase
.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation, phaseMap FROM playercreateinfo");
2740 sLog
.outString(">> Loaded %u player create definitions", count
);
2741 sLog
.outErrorDb("Error loading `playercreateinfo` table or empty table.");
2742 Log::WaitBeforeContinueIfNeed();
2746 BarGoLink
bar(result
->GetRowCount());
2750 Field
* fields
= result
->Fetch();
2752 uint32 current_race
= fields
[0].GetUInt32();
2753 uint32 current_class
= fields
[1].GetUInt32();
2754 uint32 mapId
= fields
[2].GetUInt32();
2755 uint32 areaId
= fields
[3].GetUInt32();
2756 float positionX
= fields
[4].GetFloat();
2757 float positionY
= fields
[5].GetFloat();
2758 float positionZ
= fields
[6].GetFloat();
2759 float orientation
= fields
[7].GetFloat();
2760 uint16 phaseMap
= fields
[8].GetUInt16();
2762 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(current_race
);
2763 if (!rEntry
|| !((1 << (current_race
- 1)) & RACEMASK_ALL_PLAYABLE
))
2765 sLog
.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.", current_race
);
2769 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(current_class
);
2770 if (!cEntry
|| !((1 << (current_class
- 1)) & CLASSMASK_ALL_PLAYABLE
))
2772 sLog
.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.", current_class
);
2776 // accept DB data only for valid position (and non instanceable)
2777 if (!MapManager::IsValidMapCoord(mapId
, positionX
, positionY
, positionZ
, orientation
))
2779 sLog
.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class
, current_race
);
2783 if (sMapStore
.LookupEntry(mapId
)->Instanceable())
2785 sLog
.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class
, current_race
);
2789 PlayerInfo
* pInfo
= &playerInfo
[current_race
][current_class
];
2791 pInfo
->mapId
= mapId
;
2792 pInfo
->areaId
= areaId
;
2793 pInfo
->positionX
= positionX
;
2794 pInfo
->positionY
= positionY
;
2795 pInfo
->positionZ
= positionZ
;
2796 pInfo
->orientation
= orientation
;
2797 pInfo
->phaseMap
= phaseMap
;
2799 pInfo
->displayId_m
= rEntry
->model_m
;
2800 pInfo
->displayId_f
= rEntry
->model_f
;
2805 while (result
->NextRow());
2810 sLog
.outString(">> Loaded %u player create definitions", count
);
2813 // Load playercreate items
2816 QueryResult
* result
= WorldDatabase
.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
2827 sLog
.outString(">> Loaded %u custom player create items", count
);
2831 BarGoLink
bar(result
->GetRowCount());
2835 Field
* fields
= result
->Fetch();
2837 uint32 current_race
= fields
[0].GetUInt32();
2838 uint32 current_class
= fields
[1].GetUInt32();
2840 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(current_race
);
2841 if (!rEntry
|| !((1 << (current_race
- 1)) & RACEMASK_ALL_PLAYABLE
))
2843 sLog
.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race
);
2847 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(current_class
);
2848 if (!cEntry
|| !((1 << (current_class
- 1)) & CLASSMASK_ALL_PLAYABLE
))
2850 sLog
.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class
);
2854 PlayerInfo
* pInfo
= &playerInfo
[current_race
][current_class
];
2856 uint32 item_id
= fields
[2].GetUInt32();
2858 if (!GetItemPrototype(item_id
))
2860 sLog
.outErrorDb("Item id %u (race %u class %u) in `playercreateinfo_item` table but not listed in `item_template`, ignoring.", item_id
, current_race
, current_class
);
2864 uint32 amount
= fields
[3].GetUInt32();
2868 sLog
.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.", item_id
, current_race
, current_class
);
2872 pInfo
->item
.push_back(PlayerCreateInfoItem(item_id
, amount
));
2877 while (result
->NextRow());
2882 sLog
.outString(">> Loaded %u custom player create items", count
);
2886 // Load playercreate spells
2889 QueryResult
* result
= WorldDatabase
.Query("SELECT race, class, Spell FROM playercreateinfo_spell");
2898 sLog
.outString(">> Loaded %u player create spells", count
);
2899 sLog
.outErrorDb("Error loading `playercreateinfo_spell` table or empty table.");
2903 BarGoLink
bar(result
->GetRowCount());
2907 Field
* fields
= result
->Fetch();
2909 uint32 current_race
= fields
[0].GetUInt32();
2910 uint32 current_class
= fields
[1].GetUInt32();
2912 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(current_race
);
2913 if (!rEntry
|| !((1 << (current_race
- 1)) & RACEMASK_ALL_PLAYABLE
))
2915 sLog
.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.", current_race
);
2919 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(current_class
);
2920 if (!cEntry
|| !((1 << (current_class
- 1)) & CLASSMASK_ALL_PLAYABLE
))
2922 sLog
.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.", current_class
);
2926 uint32 spell_id
= fields
[2].GetUInt32();
2927 if (!sSpellStore
.LookupEntry(spell_id
))
2929 sLog
.outErrorDb("Non existing spell %u in `playercreateinfo_spell` table, ignoring.", spell_id
);
2933 PlayerInfo
* pInfo
= &playerInfo
[current_race
][current_class
];
2934 pInfo
->spell
.push_back(spell_id
);
2939 while (result
->NextRow());
2944 sLog
.outString(">> Loaded %u player create spells", count
);
2948 // Load playercreate actions
2951 QueryResult
* result
= WorldDatabase
.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
2960 sLog
.outString(">> Loaded %u player create actions", count
);
2961 sLog
.outErrorDb("Error loading `playercreateinfo_action` table or empty table.");
2965 BarGoLink
bar(result
->GetRowCount());
2969 Field
* fields
= result
->Fetch();
2971 uint32 current_race
= fields
[0].GetUInt32();
2972 uint32 current_class
= fields
[1].GetUInt32();
2974 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(current_race
);
2975 if (!rEntry
|| !((1 << (current_race
- 1)) & RACEMASK_ALL_PLAYABLE
))
2977 sLog
.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race
);
2981 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(current_class
);
2982 if (!cEntry
|| !((1 << (current_class
- 1)) & CLASSMASK_ALL_PLAYABLE
))
2984 sLog
.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class
);
2988 uint8 action_button
= fields
[2].GetUInt8();
2989 uint32 action
= fields
[3].GetUInt32();
2990 uint8 action_type
= fields
[4].GetUInt8();
2992 if (!Player::IsActionButtonDataValid(action_button
, action
, action_type
, NULL
))
2995 PlayerInfo
* pInfo
= &playerInfo
[current_race
][current_class
];
2996 pInfo
->action
.push_back(PlayerCreateInfoAction(action_button
, action
, action_type
));
3001 while (result
->NextRow());
3006 sLog
.outString(">> Loaded %u player create actions", count
);
3010 // Loading levels data (class/race dependent)
3013 QueryResult
* result
= WorldDatabase
.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
3022 sLog
.outString(">> Loaded %u level stats definitions", count
);
3023 sLog
.outErrorDb("Error loading `player_levelstats` table or empty table.");
3024 Log::WaitBeforeContinueIfNeed();
3028 BarGoLink
bar(result
->GetRowCount());
3032 Field
* fields
= result
->Fetch();
3034 uint32 current_race
= fields
[0].GetUInt32();
3035 uint32 current_class
= fields
[1].GetUInt32();
3037 ChrRacesEntry
const* rEntry
= sChrRacesStore
.LookupEntry(current_race
);
3038 if (!rEntry
|| !((1 << (current_race
- 1)) & RACEMASK_ALL_PLAYABLE
))
3040 sLog
.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.", current_race
);
3044 ChrClassesEntry
const* cEntry
= sChrClassesStore
.LookupEntry(current_class
);
3045 if (!cEntry
|| !((1 << (current_class
- 1)) & CLASSMASK_ALL_PLAYABLE
))
3047 sLog
.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.", current_class
);
3051 uint32 current_level
= fields
[2].GetUInt32();
3052 if (current_level
> sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
3054 if (current_level
> STRONG_MAX_LEVEL
) // hardcoded level maximum
3055 sLog
.outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL
, current_level
);
3058 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.", current_level
);
3059 ++count
; // make result loading percent "expected" correct in case disabled detail mode for example.
3064 PlayerInfo
* pInfo
= &playerInfo
[current_race
][current_class
];
3066 if (!pInfo
->levelInfo
)
3067 pInfo
->levelInfo
= new PlayerLevelInfo
[sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
)];
3069 PlayerLevelInfo
* pLevelInfo
= &pInfo
->levelInfo
[current_level
- 1];
3071 for (int i
= 0; i
< MAX_STATS
; ++i
)
3072 pLevelInfo
->stats
[i
] = fields
[i
+ 3].GetUInt8();
3077 while (result
->NextRow());
3082 sLog
.outString(">> Loaded %u level stats definitions", count
);
3085 // Fill gaps and check integrity
3086 for (int race
= 0; race
< MAX_RACES
; ++race
)
3088 // skip nonexistent races
3089 if (!((1 << (race
- 1)) & RACEMASK_ALL_PLAYABLE
) || !sChrRacesStore
.LookupEntry(race
))
3092 for (int class_
= 0; class_
< MAX_CLASSES
; ++class_
)
3094 // skip nonexistent classes
3095 if (!((1 << (class_
- 1)) & CLASSMASK_ALL_PLAYABLE
) || !sChrClassesStore
.LookupEntry(class_
))
3098 PlayerInfo
* pInfo
= &playerInfo
[race
][class_
];
3100 // skip non loaded combinations
3101 if (!pInfo
->displayId_m
|| !pInfo
->displayId_f
)
3104 // skip expansion races if not playing with expansion
3105 if (sWorld
.getConfig(CONFIG_UINT32_EXPANSION
) < 1 && (race
== RACE_BLOODELF
|| race
== RACE_DRAENEI
))
3108 // skip expansion classes if not playing with expansion
3109 if (sWorld
.getConfig(CONFIG_UINT32_EXPANSION
) < 2 && class_
== CLASS_DEATH_KNIGHT
)
3112 // skip expansion races if not playing with expansion
3113 if (sWorld
.getConfig(CONFIG_UINT32_EXPANSION
) < 3 && (race
== RACE_WORGEN
|| race
== RACE_GOBLIN
))
3116 // fatal error if no level 1 data
3117 if (!pInfo
->levelInfo
|| pInfo
->levelInfo
[0].stats
[0] == 0)
3119 sLog
.outErrorDb("Race %i Class %i Level 1 does not have stats data!", race
, class_
);
3120 Log::WaitBeforeContinueIfNeed();
3125 for (uint32 level
= 1; level
< sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
); ++level
)
3127 if (pInfo
->levelInfo
[level
].stats
[0] == 0)
3129 sLog
.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race
, class_
, level
+ 1, level
);
3130 pInfo
->levelInfo
[level
] = pInfo
->levelInfo
[level
- 1];
3136 // Loading xp per level data
3138 mPlayerXPperLevel
.resize(sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
));
3139 for (uint32 level
= 0; level
< sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
); ++level
)
3140 mPlayerXPperLevel
[level
] = 0;
3143 QueryResult
* result
= WorldDatabase
.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
3152 sLog
.outString(">> Loaded %u xp for level definitions", count
);
3153 sLog
.outErrorDb("Error loading `player_xp_for_level` table or empty table.");
3154 Log::WaitBeforeContinueIfNeed();
3158 BarGoLink
bar(result
->GetRowCount());
3162 Field
* fields
= result
->Fetch();
3164 uint32 current_level
= fields
[0].GetUInt32();
3165 uint32 current_xp
= fields
[1].GetUInt32();
3167 if (current_level
>= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
3169 if (current_level
> STRONG_MAX_LEVEL
) // hardcoded level maximum
3170 sLog
.outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL
, current_level
);
3173 DETAIL_LOG("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level
);
3174 ++count
; // make result loading percent "expected" correct in case disabled detail mode for example.
3179 mPlayerXPperLevel
[current_level
] = current_xp
;
3183 while (result
->NextRow());
3188 sLog
.outString(">> Loaded %u xp for level definitions", count
);
3192 for (uint32 level
= 1; level
< sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
); ++level
)
3194 if (mPlayerXPperLevel
[level
] == 0)
3196 sLog
.outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.", level
+ 1, level
);
3197 mPlayerXPperLevel
[level
] = mPlayerXPperLevel
[level
- 1] + 100;
3202 void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_
, uint32 level
, uint32
& baseHP
, uint32
& baseMana
) const
3204 if (level
< 1 || class_
>= MAX_CLASSES
)
3207 if (level
> sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
3208 level
= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
);
3210 GtOCTBaseHPByClassEntry
const* hp
= sGtOCTBaseHPByClassStore
.LookupEntry((class_
-1) * GT_MAX_LEVEL
+ level
-1);
3211 GtOCTBaseMPByClassEntry
const* mp
= sGtOCTBaseMPByClassStore
.LookupEntry((class_
-1) * GT_MAX_LEVEL
+ level
-1);
3215 sLog
.outError("Tried to get non-existant Class-Level combination data for base hp/mp. Class %u Level %u", class_
, level
);
3219 baseHP
= uint32(hp
->ratio
);
3220 baseMana
= uint32(mp
->ratio
);
3223 void ObjectMgr::GetPlayerLevelInfo(uint32 race
, uint32 class_
, uint32 level
, PlayerLevelInfo
* info
) const
3225 if (level
< 1 || race
>= MAX_RACES
|| class_
>= MAX_CLASSES
)
3228 PlayerInfo
const* pInfo
= &playerInfo
[race
][class_
];
3229 if (pInfo
->displayId_m
== 0 || pInfo
->displayId_f
== 0)
3232 if (level
<= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
3233 *info
= pInfo
->levelInfo
[level
- 1];
3235 BuildPlayerLevelInfo(race
, class_
, level
, info
);
3238 void ObjectMgr::BuildPlayerLevelInfo(uint8 race
, uint8 _class
, uint8 level
, PlayerLevelInfo
* info
) const
3240 // base data (last known level)
3241 *info
= playerInfo
[race
][_class
].levelInfo
[sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
) - 1];
3243 for (int lvl
= sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
) - 1; lvl
< level
; ++lvl
)
3248 info
->stats
[STAT_STRENGTH
] += (lvl
> 23 ? 2 : (lvl
> 1 ? 1 : 0));
3249 info
->stats
[STAT_STAMINA
] += (lvl
> 23 ? 2 : (lvl
> 1 ? 1 : 0));
3250 info
->stats
[STAT_AGILITY
] += (lvl
> 36 ? 1 : (lvl
> 6 && (lvl
% 2) ? 1 : 0));
3251 info
->stats
[STAT_INTELLECT
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3252 info
->stats
[STAT_SPIRIT
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3255 info
->stats
[STAT_STRENGTH
] += (lvl
> 3 ? 1 : 0);
3256 info
->stats
[STAT_STAMINA
] += (lvl
> 33 ? 2 : (lvl
> 1 ? 1 : 0));
3257 info
->stats
[STAT_AGILITY
] += (lvl
> 38 ? 1 : (lvl
> 7 && !(lvl
% 2) ? 1 : 0));
3258 info
->stats
[STAT_INTELLECT
] += (lvl
> 6 && (lvl
% 2) ? 1 : 0);
3259 info
->stats
[STAT_SPIRIT
] += (lvl
> 7 ? 1 : 0);
3262 info
->stats
[STAT_STRENGTH
] += (lvl
> 4 ? 1 : 0);
3263 info
->stats
[STAT_STAMINA
] += (lvl
> 4 ? 1 : 0);
3264 info
->stats
[STAT_AGILITY
] += (lvl
> 33 ? 2 : (lvl
> 1 ? 1 : 0));
3265 info
->stats
[STAT_INTELLECT
] += (lvl
> 8 && (lvl
% 2) ? 1 : 0);
3266 info
->stats
[STAT_SPIRIT
] += (lvl
> 38 ? 1 : (lvl
> 9 && !(lvl
% 2) ? 1 : 0));
3269 info
->stats
[STAT_STRENGTH
] += (lvl
> 5 ? 1 : 0);
3270 info
->stats
[STAT_STAMINA
] += (lvl
> 4 ? 1 : 0);
3271 info
->stats
[STAT_AGILITY
] += (lvl
> 16 ? 2 : (lvl
> 1 ? 1 : 0));
3272 info
->stats
[STAT_INTELLECT
] += (lvl
> 8 && !(lvl
% 2) ? 1 : 0);
3273 info
->stats
[STAT_SPIRIT
] += (lvl
> 38 ? 1 : (lvl
> 9 && !(lvl
% 2) ? 1 : 0));
3276 info
->stats
[STAT_STRENGTH
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3277 info
->stats
[STAT_STAMINA
] += (lvl
> 5 ? 1 : 0);
3278 info
->stats
[STAT_AGILITY
] += (lvl
> 38 ? 1 : (lvl
> 8 && (lvl
% 2) ? 1 : 0));
3279 info
->stats
[STAT_INTELLECT
] += (lvl
> 22 ? 2 : (lvl
> 1 ? 1 : 0));
3280 info
->stats
[STAT_SPIRIT
] += (lvl
> 3 ? 1 : 0);
3283 info
->stats
[STAT_STRENGTH
] += (lvl
> 34 ? 1 : (lvl
> 6 && (lvl
% 2) ? 1 : 0));
3284 info
->stats
[STAT_STAMINA
] += (lvl
> 4 ? 1 : 0);
3285 info
->stats
[STAT_AGILITY
] += (lvl
> 7 && !(lvl
% 2) ? 1 : 0);
3286 info
->stats
[STAT_INTELLECT
] += (lvl
> 5 ? 1 : 0);
3287 info
->stats
[STAT_SPIRIT
] += (lvl
> 4 ? 1 : 0);
3290 info
->stats
[STAT_STRENGTH
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3291 info
->stats
[STAT_STAMINA
] += (lvl
> 5 ? 1 : 0);
3292 info
->stats
[STAT_AGILITY
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3293 info
->stats
[STAT_INTELLECT
] += (lvl
> 24 ? 2 : (lvl
> 1 ? 1 : 0));
3294 info
->stats
[STAT_SPIRIT
] += (lvl
> 33 ? 2 : (lvl
> 2 ? 1 : 0));
3297 info
->stats
[STAT_STRENGTH
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3298 info
->stats
[STAT_STAMINA
] += (lvl
> 38 ? 2 : (lvl
> 3 ? 1 : 0));
3299 info
->stats
[STAT_AGILITY
] += (lvl
> 9 && !(lvl
% 2) ? 1 : 0);
3300 info
->stats
[STAT_INTELLECT
] += (lvl
> 33 ? 2 : (lvl
> 2 ? 1 : 0));
3301 info
->stats
[STAT_SPIRIT
] += (lvl
> 38 ? 2 : (lvl
> 3 ? 1 : 0));
3304 info
->stats
[STAT_STRENGTH
] += (lvl
> 38 ? 2 : (lvl
> 6 && (lvl
% 2) ? 1 : 0));
3305 info
->stats
[STAT_STAMINA
] += (lvl
> 32 ? 2 : (lvl
> 4 ? 1 : 0));
3306 info
->stats
[STAT_AGILITY
] += (lvl
> 38 ? 2 : (lvl
> 8 && (lvl
% 2) ? 1 : 0));
3307 info
->stats
[STAT_INTELLECT
] += (lvl
> 38 ? 3 : (lvl
> 4 ? 1 : 0));
3308 info
->stats
[STAT_SPIRIT
] += (lvl
> 38 ? 3 : (lvl
> 5 ? 1 : 0));
3313 /* ********************************************************************************************* */
3314 /* * Static Wrappers */
3315 /* ********************************************************************************************* */
3316 GameObjectInfo
const* ObjectMgr::GetGameObjectInfo(uint32 id
) { return sGOStorage
.LookupEntry
<GameObjectInfo
>(id
); }
3317 Player
* ObjectMgr::GetPlayer(const char* name
) { return ObjectAccessor::FindPlayerByName(name
); }
3318 Player
* ObjectMgr::GetPlayer(ObjectGuid guid
, bool inWorld
/*=true*/) { return ObjectAccessor::FindPlayer(guid
, inWorld
); }
3319 CreatureInfo
const* ObjectMgr::GetCreatureTemplate(uint32 id
) { return sCreatureStorage
.LookupEntry
<CreatureInfo
>(id
); }
3320 CreatureModelInfo
const* ObjectMgr::GetCreatureModelInfo(uint32 modelid
) { return sCreatureModelStorage
.LookupEntry
<CreatureModelInfo
>(modelid
); }
3321 EquipmentInfo
const* ObjectMgr::GetEquipmentInfo(uint32 entry
) { return sEquipmentStorage
.LookupEntry
<EquipmentInfo
>(entry
); }
3322 CreatureDataAddon
const* ObjectMgr::GetCreatureAddon(uint32 lowguid
) { return sCreatureDataAddonStorage
.LookupEntry
<CreatureDataAddon
>(lowguid
); }
3323 CreatureDataAddon
const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry
) { return sCreatureInfoAddonStorage
.LookupEntry
<CreatureDataAddon
>(entry
); }
3324 ItemPrototype
const* ObjectMgr::GetItemPrototype(uint32 id
) { return sItemStorage
.LookupEntry
<ItemPrototype
>(id
); }
3325 InstanceTemplate
const* ObjectMgr::GetInstanceTemplate(uint32 map
) { return sInstanceTemplate
.LookupEntry
<InstanceTemplate
>(map
); }
3326 WorldTemplate
const* ObjectMgr::GetWorldTemplate(uint32 map
) { return sWorldTemplate
.LookupEntry
<WorldTemplate
>(map
); }
3328 /* ********************************************************************************************* */
3329 /* * Loading Functions */
3330 /* ********************************************************************************************* */
3331 void ObjectMgr::LoadArenaTeams()
3336 QueryResult
* result
= CharacterDatabase
.Query("SELECT arena_team.arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,"
3337 // 6 7 8 9 10 11 12 13 14
3338 "EmblemColor,BorderStyle,BorderColor, rating,games_week,wins_week,games_season,wins_season,rank "
3339 "FROM arena_team LEFT JOIN arena_team_stats ON arena_team.arenateamid = arena_team_stats.arenateamid ORDER BY arena_team.arenateamid ASC");
3349 sLog
.outString(">> Loaded %u arenateam definitions", count
);
3353 // load arena_team members
3354 QueryResult
* arenaTeamMembersResult
= CharacterDatabase
.Query(
3355 // 0 1 2 3 4 5 6 7 8
3356 "SELECT arenateamid,member.guid,played_week,wons_week,played_season,wons_season,personal_rating,name,class "
3357 "FROM arena_team_member member LEFT JOIN characters chars on member.guid = chars.guid ORDER BY member.arenateamid ASC");
3359 BarGoLink
bar(result
->GetRowCount());
3367 ArenaTeam
* newArenaTeam
= new ArenaTeam
;
3368 if (!newArenaTeam
->LoadArenaTeamFromDB(result
) ||
3369 !newArenaTeam
->LoadMembersFromDB(arenaTeamMembersResult
))
3371 newArenaTeam
->Disband(NULL
);
3372 delete newArenaTeam
;
3375 AddArenaTeam(newArenaTeam
);
3377 while (result
->NextRow());
3380 delete arenaTeamMembersResult
;
3383 sLog
.outString(">> Loaded %u arenateam definitions", count
);
3386 void ObjectMgr::LoadGroups()
3388 // -- loading groups --
3390 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
3391 QueryResult
* result
= CharacterDatabase
.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raiddifficulty, leaderGuid, groupId FROM groups");
3400 sLog
.outString(">> Loaded %u group definitions", count
);
3404 BarGoLink
bar(result
->GetRowCount());
3409 Field
* fields
= result
->Fetch();
3411 Group
* group
= new Group
;
3412 if (!group
->LoadGroupFromDB(fields
))
3420 while (result
->NextRow());
3425 sLog
.outString(">> Loaded %u group definitions", count
);
3427 // -- loading members --
3430 result
= CharacterDatabase
.Query("SELECT memberGuid, assistant, subgroup, groupId FROM group_member ORDER BY groupId");
3438 Group
* group
= NULL
; // used as cached pointer for avoid relookup group for each member
3440 BarGoLink
bar2(result
->GetRowCount());
3444 Field
* fields
= result
->Fetch();
3447 uint32 memberGuidlow
= fields
[0].GetUInt32();
3448 ObjectGuid memberGuid
= ObjectGuid(HIGHGUID_PLAYER
, memberGuidlow
);
3449 bool assistent
= fields
[1].GetBool();
3450 uint8 subgroup
= fields
[2].GetUInt8();
3451 uint32 groupId
= fields
[3].GetUInt32();
3452 if (!group
|| group
->GetId() != groupId
)
3454 group
= GetGroupById(groupId
);
3457 sLog
.outErrorDb("Incorrect entry in group_member table : no group with Id %d for member %s!",
3458 groupId
, memberGuid
.GetString().c_str());
3459 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE memberGuid = '%u'", memberGuidlow
);
3464 if (!group
->LoadMemberFromDB(memberGuidlow
, subgroup
, assistent
))
3466 sLog
.outErrorDb("Incorrect entry in group_member table : member %s cannot be added to group (Id: %u)!",
3467 memberGuid
.GetString().c_str(), groupId
);
3468 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE memberGuid = '%u'", memberGuidlow
);
3471 while (result
->NextRow());
3476 // TODO: maybe delete from the DB before loading in this case
3477 for (GroupMap::iterator itr
= mGroupMap
.begin(); itr
!= mGroupMap
.end();)
3479 if (itr
->second
->GetMembersCount() < 2)
3481 itr
->second
->Disband();
3483 mGroupMap
.erase(itr
++);
3489 // -- loading instances --
3491 result
= CharacterDatabase
.Query(
3493 "SELECT group_instance.leaderGuid, map, instance, permanent, instance.difficulty, resettime, "
3495 "(SELECT COUNT(*) FROM character_instance WHERE guid = group_instance.leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1), "
3497 " groups.groupId, instance.encountersMask "
3498 "FROM group_instance LEFT JOIN instance ON instance = id LEFT JOIN groups ON groups.leaderGUID = group_instance.leaderGUID ORDER BY leaderGuid"
3508 Group
* group
= NULL
; // used as cached pointer for avoid relookup group for each member
3510 BarGoLink
bar2(result
->GetRowCount());
3514 Field
* fields
= result
->Fetch();
3517 uint32 leaderGuidLow
= fields
[0].GetUInt32();
3518 uint32 mapId
= fields
[1].GetUInt32();
3519 Difficulty diff
= (Difficulty
)fields
[4].GetUInt8();
3520 uint32 groupId
= fields
[7].GetUInt32();
3522 if (!group
|| group
->GetId() != groupId
)
3524 // find group id in map by leader low guid
3525 group
= GetGroupById(groupId
);
3528 sLog
.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", leaderGuidLow
);
3533 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(mapId
);
3534 if (!mapEntry
|| !mapEntry
->IsDungeon())
3536 sLog
.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", mapId
);
3540 if (diff
>= (mapEntry
->IsRaid() ? MAX_RAID_DIFFICULTY
: MAX_DUNGEON_DIFFICULTY
))
3542 sLog
.outErrorDb("Wrong dungeon difficulty use in group_instance table: %d", diff
+ 1);
3543 diff
= REGULAR_DIFFICULTY
; // default for both difficaly types
3546 DungeonPersistentState
* state
= (DungeonPersistentState
*)sMapPersistentStateMgr
.AddPersistentState(mapEntry
, fields
[2].GetUInt32(), Difficulty(diff
), (time_t)fields
[5].GetUInt64(), (fields
[6].GetUInt32() == 0), true, true, fields
[8].GetUInt32());
3547 group
->BindToInstance(state
, fields
[3].GetBool(), true);
3549 while (result
->NextRow());
3554 sLog
.outString(">> Loaded %u group-instance binds total", count
);
3557 sLog
.outString(">> Loaded %u group members total", count
);
3560 void ObjectMgr::LoadQuests()
3563 for (QuestMap::const_iterator itr
= mQuestTemplates
.begin(); itr
!= mQuestTemplates
.end(); ++itr
)
3566 mQuestTemplates
.clear();
3568 m_ExclusiveQuestGroups
.clear();
3570 // 0 1 2 3 4 5 6 7 8 9
3571 QueryResult
* result
= WorldDatabase
.Query("SELECT entry, Method, ZoneOrSort, MinLevel, QuestLevel, Type, RequiredClasses, RequiredRaces, RequiredSkill, RequiredSkillValue,"
3572 // 10 11 12 13 14 15 16 17
3573 "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
3574 // 18 19 20 21 22 23 24 25 26
3575 "QuestFlags, SpecialFlags, CharTitleId, PlayersSlain, BonusTalents, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain,"
3577 "RewXPId, SrcItemId, SrcItemCount, SrcSpell,"
3578 // 31 32 33 34 35 36 37 38 39 40 41
3579 "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, CompletedText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
3580 // 42 43 44 45 46 47 48 49 50 51 52 53
3581 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemId5, ReqItemId6, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4, ReqItemCount5, ReqItemCount6,"
3582 // 54 55 56 57 58 59 60 61
3583 "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4,"
3584 // 62 63 64 65 66 67 68 69
3585 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
3587 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
3588 // 74 75 76 77 78 79
3589 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
3590 // 80 81 82 83 84 85
3591 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
3592 // 86 87 88 89 90 91 92 93
3593 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
3595 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5,"
3596 // 99 100 101 102 103
3597 "RewRepValueId1, RewRepValueId2, RewRepValueId3, RewRepValueId4, RewRepValueId5,"
3598 // 104 105 106 107 108
3599 "RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
3600 // 109 110 111 112 113 114
3601 "RewHonorAddition, RewHonorMultiplier, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast,"
3602 // 115 116 117 118 119 120
3603 "RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
3604 // 121 122 123 124 125 126 127 128
3605 "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4,"
3606 // 129 130 131 132 133 134
3607 "IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
3609 "OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4,"
3611 "StartScript, CompleteScript, "
3612 // 141 142 143 144 145 146 147
3613 "ReqSpellLearned, PortraitGiver, PortraitTurnIn, PortraitGiverName, PortraitGiverText, PortraitTurnInName, PortraitTurnInText, "
3614 // 148 149 150 151 152 153 154 155
3615 "ReqCurrencyId1, ReqCurrencyId2, ReqCurrencyId3, ReqCurrencyId4, ReqCurrencyCount1, ReqCurrencyCount2, ReqCurrencyCount3, ReqCurrencyCount4, "
3616 // 156 157 158 159 160 161 162 163
3617 "RewCurrencyId1, RewCurrencyId2, RewCurrencyId3, RewCurrencyId4, RewCurrencyCount1, RewCurrencyCount2, RewCurrencyCount3, RewCurrencyCount4, "
3619 "RewSkill, RewSkillValue, SoundAccept, SoundTurnIn "
3620 " FROM quest_template");
3627 sLog
.outString(">> Loaded 0 quests definitions");
3628 sLog
.outErrorDb("`quest_template` table is empty!");
3632 // create multimap previous quest for each existing quest
3633 // some quests can have many previous maps set by NextQuestId in previous quest
3634 // for example set of race quests can lead to single not race specific quest
3635 BarGoLink
bar(result
->GetRowCount());
3639 Field
* fields
= result
->Fetch();
3641 Quest
* newQuest
= new Quest(fields
);
3642 mQuestTemplates
[newQuest
->GetQuestId()] = newQuest
;
3644 while (result
->NextRow());
3650 std::map
<uint32
, uint32
> usedMailTemplates
;
3652 for (QuestMap::iterator iter
= mQuestTemplates
.begin(); iter
!= mQuestTemplates
.end(); ++iter
)
3654 Quest
* qinfo
= iter
->second
;
3656 // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
3658 if (qinfo
->GetQuestMethod() >= 3)
3660 sLog
.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo
->GetQuestId(), qinfo
->GetQuestMethod());
3663 if (qinfo
->m_SpecialFlags
> QUEST_SPECIAL_FLAG_DB_ALLOWED
)
3665 sLog
.outErrorDb("Quest %u has `SpecialFlags` = %u, above max flags not allowed for database.", qinfo
->GetQuestId(), qinfo
->m_SpecialFlags
);
3668 if (qinfo
->HasQuestFlag(QUEST_FLAGS_DAILY
) && qinfo
->HasQuestFlag(QUEST_FLAGS_WEEKLY
))
3670 sLog
.outErrorDb("Weekly Quest %u is marked as daily quest in `QuestFlags`, removed daily flag.", qinfo
->GetQuestId());
3671 qinfo
->m_QuestFlags
&= ~QUEST_FLAGS_DAILY
;
3674 if (qinfo
->HasQuestFlag(QUEST_FLAGS_DAILY
))
3676 if (!qinfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE
))
3678 sLog
.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo
->GetQuestId());
3679 qinfo
->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE
);
3683 if (qinfo
->HasQuestFlag(QUEST_FLAGS_WEEKLY
))
3685 if (!qinfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE
))
3687 sLog
.outErrorDb("Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo
->GetQuestId());
3688 qinfo
->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE
);
3692 if (qinfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_MONTHLY
))
3694 if (!qinfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE
))
3696 sLog
.outErrorDb("Monthly quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo
->GetQuestId());
3697 qinfo
->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE
);
3701 if (qinfo
->HasQuestFlag(QUEST_FLAGS_AUTO_REWARDED
))
3703 // at auto-reward can be rewarded only RewChoiceItemId[0]
3704 for (int j
= 1; j
< QUEST_REWARD_CHOICES_COUNT
; ++j
)
3706 if (uint32 id
= qinfo
->RewChoiceItemId
[j
])
3708 sLog
.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
3709 qinfo
->GetQuestId(), j
+ 1, id
, j
+ 1);
3710 // no changes, quest ignore this data
3715 // client quest log visual (area case)
3716 if (qinfo
->ZoneOrSort
> 0)
3718 if (!GetAreaEntryByAreaID(qinfo
->ZoneOrSort
))
3720 sLog
.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
3721 qinfo
->GetQuestId(), qinfo
->ZoneOrSort
);
3722 // no changes, quest not dependent from this value but can have problems at client
3725 // client quest log visual (sort case)
3726 if (qinfo
->ZoneOrSort
< 0)
3728 QuestSortEntry
const* qSort
= sQuestSortStore
.LookupEntry(-int32(qinfo
->ZoneOrSort
));
3731 sLog
.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
3732 qinfo
->GetQuestId(), qinfo
->ZoneOrSort
);
3733 // 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)
3737 // RequiredClasses, can be 0/CLASSMASK_ALL_PLAYABLE to allow any class
3738 if (qinfo
->RequiredClasses
)
3740 if (!(qinfo
->RequiredClasses
& CLASSMASK_ALL_PLAYABLE
))
3742 sLog
.outErrorDb("Quest %u does not contain any playable classes in `RequiredClasses` (%u), value set to 0 (all classes).", qinfo
->GetQuestId(), qinfo
->RequiredClasses
);
3743 qinfo
->RequiredClasses
= 0;
3747 // RequiredRaces, can be 0/RACEMASK_ALL_PLAYABLE to allow any race
3748 if (qinfo
->RequiredRaces
)
3750 if (!(qinfo
->RequiredRaces
& RACEMASK_ALL_PLAYABLE
))
3752 sLog
.outErrorDb("Quest %u does not contain any playable races in `RequiredRaces` (%u), value set to 0 (all races).", qinfo
->GetQuestId(), qinfo
->RequiredRaces
);
3753 qinfo
->RequiredRaces
= 0;
3757 // RequiredSkill, can be 0
3758 if (qinfo
->RequiredSkill
)
3760 if (!sSkillLineStore
.LookupEntry(qinfo
->RequiredSkill
))
3762 sLog
.outErrorDb("Quest %u has `RequiredSkill` = %u but this skill does not exist",
3763 qinfo
->GetQuestId(), qinfo
->RequiredSkill
);
3767 if (qinfo
->RequiredSkillValue
)
3769 if (qinfo
->RequiredSkillValue
> sWorld
.GetConfigMaxSkillValue())
3771 sLog
.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
3772 qinfo
->GetQuestId(), qinfo
->RequiredSkillValue
, sWorld
.GetConfigMaxSkillValue());
3773 // no changes, quest can't be done for this requirement
3776 // else Skill quests can have 0 skill level, this is ok
3778 if (qinfo
->RepObjectiveFaction
&& !sFactionStore
.LookupEntry(qinfo
->RepObjectiveFaction
))
3780 sLog
.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
3781 qinfo
->GetQuestId(), qinfo
->RepObjectiveFaction
, qinfo
->RepObjectiveFaction
);
3782 // no changes, quest can't be done for this requirement
3785 if (qinfo
->RequiredMinRepFaction
&& !sFactionStore
.LookupEntry(qinfo
->RequiredMinRepFaction
))
3787 sLog
.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3788 qinfo
->GetQuestId(), qinfo
->RequiredMinRepFaction
, qinfo
->RequiredMinRepFaction
);
3789 // no changes, quest can't be done for this requirement
3792 if (qinfo
->RequiredMaxRepFaction
&& !sFactionStore
.LookupEntry(qinfo
->RequiredMaxRepFaction
))
3794 sLog
.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3795 qinfo
->GetQuestId(), qinfo
->RequiredMaxRepFaction
, qinfo
->RequiredMaxRepFaction
);
3796 // no changes, quest can't be done for this requirement
3799 if (qinfo
->RequiredMinRepValue
&& qinfo
->RequiredMinRepValue
> ReputationMgr::Reputation_Cap
)
3801 sLog
.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
3802 qinfo
->GetQuestId(), qinfo
->RequiredMinRepValue
, ReputationMgr::Reputation_Cap
);
3803 // no changes, quest can't be done for this requirement
3806 if (qinfo
->RequiredMinRepValue
&& qinfo
->RequiredMaxRepValue
&& qinfo
->RequiredMaxRepValue
<= qinfo
->RequiredMinRepValue
)
3808 sLog
.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
3809 qinfo
->GetQuestId(), qinfo
->RequiredMaxRepValue
, qinfo
->RequiredMinRepValue
);
3810 // no changes, quest can't be done for this requirement
3813 if (!qinfo
->RepObjectiveFaction
&& qinfo
->RepObjectiveValue
> 0)
3815 sLog
.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
3816 qinfo
->GetQuestId(), qinfo
->RepObjectiveValue
);
3820 if (!qinfo
->RequiredMinRepFaction
&& qinfo
->RequiredMinRepValue
> 0)
3822 sLog
.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
3823 qinfo
->GetQuestId(), qinfo
->RequiredMinRepValue
);
3827 if (!qinfo
->RequiredMaxRepFaction
&& qinfo
->RequiredMaxRepValue
> 0)
3829 sLog
.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
3830 qinfo
->GetQuestId(), qinfo
->RequiredMaxRepValue
);
3834 if (qinfo
->CharTitleId
&& !sCharTitlesStore
.LookupEntry(qinfo
->CharTitleId
))
3836 sLog
.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
3837 qinfo
->GetQuestId(), qinfo
->GetCharTitleId(), qinfo
->GetCharTitleId());
3838 qinfo
->CharTitleId
= 0;
3839 // quest can't reward this title
3842 if (qinfo
->SrcItemId
)
3844 if (!sItemStorage
.LookupEntry
<ItemPrototype
>(qinfo
->SrcItemId
))
3846 sLog
.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
3847 qinfo
->GetQuestId(), qinfo
->SrcItemId
, qinfo
->SrcItemId
);
3848 qinfo
->SrcItemId
= 0; // quest can't be done for this requirement
3850 else if (qinfo
->SrcItemCount
== 0)
3852 sLog
.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
3853 qinfo
->GetQuestId(), qinfo
->SrcItemId
);
3854 qinfo
->SrcItemCount
= 1; // update to 1 for allow quest work for backward compatibility with DB
3857 else if (qinfo
->SrcItemCount
> 0)
3859 sLog
.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
3860 qinfo
->GetQuestId(), qinfo
->SrcItemCount
);
3861 qinfo
->SrcItemCount
= 0; // no quest work changes in fact
3864 if (qinfo
->SrcSpell
)
3866 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(qinfo
->SrcSpell
);
3869 sLog
.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
3870 qinfo
->GetQuestId(), qinfo
->SrcSpell
, qinfo
->SrcSpell
);
3871 qinfo
->SrcSpell
= 0; // quest can't be done for this requirement
3873 else if (!SpellMgr::IsSpellValid(spellInfo
))
3875 sLog
.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
3876 qinfo
->GetQuestId(), qinfo
->SrcSpell
, qinfo
->SrcSpell
);
3877 qinfo
->SrcSpell
= 0; // quest can't be done for this requirement
3881 for (int j
= 0; j
< QUEST_ITEM_OBJECTIVES_COUNT
; ++j
)
3883 if (uint32 id
= qinfo
->ReqItemId
[j
])
3885 if (qinfo
->ReqItemCount
[j
] == 0)
3887 sLog
.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
3888 qinfo
->GetQuestId(), j
+ 1, id
, j
+ 1);
3889 // no changes, quest can't be done for this requirement
3892 qinfo
->SetSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
);
3894 if (!sItemStorage
.LookupEntry
<ItemPrototype
>(id
))
3896 sLog
.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3897 qinfo
->GetQuestId(), j
+ 1, id
, id
);
3898 qinfo
->ReqItemCount
[j
] = 0; // prevent incorrect work of quest
3901 else if (qinfo
->ReqItemCount
[j
] > 0)
3903 sLog
.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
3904 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->ReqItemCount
[j
]);
3905 qinfo
->ReqItemCount
[j
] = 0; // prevent incorrect work of quest
3909 for (int j
= 0; j
< QUEST_SOURCE_ITEM_IDS_COUNT
; ++j
)
3911 if (uint32 id
= qinfo
->ReqSourceId
[j
])
3913 if (!sItemStorage
.LookupEntry
<ItemPrototype
>(id
))
3915 sLog
.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
3916 qinfo
->GetQuestId(), j
+ 1, id
, id
);
3917 // no changes, quest can't be done for this requirement
3922 if (qinfo
->ReqSourceCount
[j
] > 0)
3924 sLog
.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
3925 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->ReqSourceCount
[j
]);
3926 // no changes, quest ignore this data
3931 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
3933 if (uint32 id
= qinfo
->ReqSpell
[j
])
3935 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(id
);
3938 sLog
.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
3939 qinfo
->GetQuestId(), j
+ 1, id
, id
);
3943 if (!qinfo
->ReqCreatureOrGOId
[j
])
3946 for (int k
= 0; k
< MAX_EFFECT_INDEX
; ++k
)
3948 SpellEffectEntry
const* spellEffect
= spellInfo
->GetSpellEffect(SpellEffectIndex(k
));
3952 if ((spellEffect
->Effect
== SPELL_EFFECT_QUEST_COMPLETE
&& uint32(spellEffect
->EffectMiscValue
) == qinfo
->QuestId
) ||
3953 spellEffect
->Effect
== SPELL_EFFECT_SEND_EVENT
)
3962 if (!qinfo
->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT
))
3964 sLog
.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.", spellInfo
->Id
, qinfo
->QuestId
, j
+ 1, j
+ 1);
3966 // this will prevent quest completing without objective
3967 const_cast<Quest
*>(qinfo
)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT
);
3972 sLog
.outErrorDb("Quest %u has `ReqSpellCast%d` = %u and ReqCreatureOrGOId%d = 0 but spell %u does not have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT effect for this quest, quest can't be done.",
3973 qinfo
->GetQuestId(), j
+ 1, id
, j
+ 1, id
);
3974 // no changes, quest can't be done for this requirement
3980 for (int j
= 0; j
< QUEST_OBJECTIVES_COUNT
; ++j
)
3982 int32 id
= qinfo
->ReqCreatureOrGOId
[j
];
3983 if (id
< 0 && !sGOStorage
.LookupEntry
<GameObjectInfo
>(-id
))
3985 sLog
.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
3986 qinfo
->GetQuestId(), j
+ 1, id
, uint32(-id
));
3987 qinfo
->ReqCreatureOrGOId
[j
] = 0; // quest can't be done for this requirement
3990 if (id
> 0 && !sCreatureStorage
.LookupEntry
<CreatureInfo
>(id
))
3992 sLog
.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
3993 qinfo
->GetQuestId(), j
+ 1, id
, uint32(id
));
3994 qinfo
->ReqCreatureOrGOId
[j
] = 0; // quest can't be done for this requirement
3999 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
4001 qinfo
->SetSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST
| QUEST_SPECIAL_FLAG_SPEAKTO
));
4003 if (!qinfo
->ReqCreatureOrGOCount
[j
])
4005 sLog
.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
4006 qinfo
->GetQuestId(), j
+ 1, id
, j
+ 1);
4007 // no changes, quest can be incorrectly done, but we already report this
4010 else if (qinfo
->ReqCreatureOrGOCount
[j
] > 0)
4012 sLog
.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
4013 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->ReqCreatureOrGOCount
[j
]);
4014 // no changes, quest ignore this data
4018 bool choice_found
= false;
4019 for (int j
= QUEST_REWARD_CHOICES_COUNT
- 1; j
>= 0; --j
)
4021 if (uint32 id
= qinfo
->RewChoiceItemId
[j
])
4023 if (!sItemStorage
.LookupEntry
<ItemPrototype
>(id
))
4025 sLog
.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
4026 qinfo
->GetQuestId(), j
+ 1, id
, id
);
4027 qinfo
->RewChoiceItemId
[j
] = 0; // no changes, quest will not reward this
4030 choice_found
= true;
4032 if (!qinfo
->RewChoiceItemCount
[j
])
4034 sLog
.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
4035 qinfo
->GetQuestId(), j
+ 1, id
, j
+ 1);
4036 // no changes, quest can't be done
4039 else if (choice_found
) // client crash if have gap in item reward choices
4041 sLog
.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemId%d` = %u, client can crash at like data.",
4042 qinfo
->GetQuestId(), j
+ 1, j
+ 2, qinfo
->RewChoiceItemId
[j
+ 1]);
4043 // fill gap by clone later filled choice
4044 qinfo
->RewChoiceItemId
[j
] = qinfo
->RewChoiceItemId
[j
+ 1];
4045 qinfo
->RewChoiceItemCount
[j
] = qinfo
->RewChoiceItemCount
[j
+ 1];
4047 else if (qinfo
->RewChoiceItemCount
[j
] > 0)
4049 sLog
.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
4050 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->RewChoiceItemCount
[j
]);
4051 // no changes, quest ignore this data
4055 for (int j
= 0; j
< QUEST_REWARDS_COUNT
; ++j
)
4057 if (uint32 id
= qinfo
->RewItemId
[j
])
4059 if (!sItemStorage
.LookupEntry
<ItemPrototype
>(id
))
4061 sLog
.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
4062 qinfo
->GetQuestId(), j
+ 1, id
, id
);
4063 qinfo
->RewItemId
[j
] = 0; // no changes, quest will not reward this item
4066 if (!qinfo
->RewItemCount
[j
])
4068 sLog
.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
4069 qinfo
->GetQuestId(), j
+ 1, id
, j
+ 1);
4073 else if (qinfo
->RewItemCount
[j
] > 0)
4075 sLog
.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
4076 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->RewItemCount
[j
]);
4077 // no changes, quest ignore this data
4081 for (int j
= 0; j
< QUEST_REPUTATIONS_COUNT
; ++j
)
4083 if (qinfo
->RewRepFaction
[j
])
4085 if (abs(qinfo
->RewRepValueId
[j
]) > 9)
4086 sLog
.outErrorDb("Quest %u has RewRepValueId%d = %i but value is not valid.", qinfo
->GetQuestId(), j
+ 1, qinfo
->RewRepValueId
[j
]);
4088 if (!sFactionStore
.LookupEntry(qinfo
->RewRepFaction
[j
]))
4090 sLog
.outErrorDb("Quest %u has `RewRepFaction%d` = %u but raw faction (faction.dbc) %u does not exist, quest will not reward reputation for this faction.",
4091 qinfo
->GetQuestId(), j
+ 1, qinfo
->RewRepFaction
[j
] , qinfo
->RewRepFaction
[j
]);
4092 qinfo
->RewRepFaction
[j
] = 0; // quest will not reward this
4095 else if (qinfo
->RewRepValue
[j
] != 0)
4097 sLog
.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %i.",
4098 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->RewRepValue
[j
]);
4099 // no changes, quest ignore this data
4103 if (qinfo
->RewSpell
)
4105 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(qinfo
->RewSpell
);
4109 sLog
.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
4110 qinfo
->GetQuestId(), qinfo
->RewSpell
, qinfo
->RewSpell
);
4111 qinfo
->RewSpell
= 0; // no spell reward will display for this quest
4113 else if (!SpellMgr::IsSpellValid(spellInfo
))
4115 sLog
.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest will not have a spell reward.",
4116 qinfo
->GetQuestId(), qinfo
->RewSpell
, qinfo
->RewSpell
);
4117 qinfo
->RewSpell
= 0; // no spell reward will display for this quest
4119 else if (GetTalentSpellCost(qinfo
->RewSpell
))
4121 sLog
.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4122 qinfo
->GetQuestId(), qinfo
->RewSpell
, qinfo
->RewSpell
);
4123 qinfo
->RewSpell
= 0; // no spell reward will display for this quest
4127 if (qinfo
->RewSpellCast
)
4129 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(qinfo
->RewSpellCast
);
4133 sLog
.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
4134 qinfo
->GetQuestId(), qinfo
->RewSpellCast
, qinfo
->RewSpellCast
);
4135 qinfo
->RewSpellCast
= 0; // no spell will be casted on player
4137 else if (!SpellMgr::IsSpellValid(spellInfo
))
4139 sLog
.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest will not have a spell reward.",
4140 qinfo
->GetQuestId(), qinfo
->RewSpellCast
, qinfo
->RewSpellCast
);
4141 qinfo
->RewSpellCast
= 0; // no spell will be casted on player
4143 else if (GetTalentSpellCost(qinfo
->RewSpellCast
))
4145 sLog
.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
4146 qinfo
->GetQuestId(), qinfo
->RewSpellCast
, qinfo
->RewSpellCast
);
4147 qinfo
->RewSpellCast
= 0; // no spell will be casted on player
4151 if (qinfo
->RewMailTemplateId
)
4153 if (!sMailTemplateStore
.LookupEntry(qinfo
->RewMailTemplateId
))
4155 sLog
.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
4156 qinfo
->GetQuestId(), qinfo
->RewMailTemplateId
, qinfo
->RewMailTemplateId
);
4157 qinfo
->RewMailTemplateId
= 0; // no mail will send to player
4158 qinfo
->RewMailDelaySecs
= 0; // no mail will send to player
4160 else if (usedMailTemplates
.find(qinfo
->RewMailTemplateId
) != usedMailTemplates
.end())
4162 std::map
<uint32
, uint32
>::const_iterator used_mt_itr
= usedMailTemplates
.find(qinfo
->RewMailTemplateId
);
4163 sLog
.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u already used for quest %u, quest will not have a mail reward.",
4164 qinfo
->GetQuestId(), qinfo
->RewMailTemplateId
, qinfo
->RewMailTemplateId
, used_mt_itr
->second
);
4165 qinfo
->RewMailTemplateId
= 0; // no mail will send to player
4166 qinfo
->RewMailDelaySecs
= 0; // no mail will send to player
4169 usedMailTemplates
[qinfo
->RewMailTemplateId
] = qinfo
->GetQuestId();
4172 if (qinfo
->NextQuestInChain
)
4174 QuestMap::iterator qNextItr
= mQuestTemplates
.find(qinfo
->NextQuestInChain
);
4175 if (qNextItr
== mQuestTemplates
.end())
4177 sLog
.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
4178 qinfo
->GetQuestId(), qinfo
->NextQuestInChain
, qinfo
->NextQuestInChain
);
4179 qinfo
->NextQuestInChain
= 0;
4182 qNextItr
->second
->prevChainQuests
.push_back(qinfo
->GetQuestId());
4185 // fill additional data stores
4186 if (qinfo
->PrevQuestId
)
4188 if (mQuestTemplates
.find(abs(qinfo
->GetPrevQuestId())) == mQuestTemplates
.end())
4190 sLog
.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo
->GetQuestId(), qinfo
->GetPrevQuestId());
4194 qinfo
->prevQuests
.push_back(qinfo
->PrevQuestId
);
4198 if (qinfo
->NextQuestId
)
4200 QuestMap::iterator qNextItr
= mQuestTemplates
.find(abs(qinfo
->GetNextQuestId()));
4201 if (qNextItr
== mQuestTemplates
.end())
4203 sLog
.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo
->GetQuestId(), qinfo
->GetNextQuestId());
4207 int32 signedQuestId
= qinfo
->NextQuestId
< 0 ? -int32(qinfo
->GetQuestId()) : int32(qinfo
->GetQuestId());
4208 qNextItr
->second
->prevQuests
.push_back(signedQuestId
);
4212 if (qinfo
->RewSkill
)
4214 if (!sSkillLineStore
.LookupEntry(qinfo
->RewSkill
))
4216 sLog
.outErrorDb("Quest %u has `RewSkill` = %u but this skill does not exist",
4217 qinfo
->GetQuestId(), qinfo
->RewSkill
);
4218 qinfo
->RewSkill
= 0;
4219 qinfo
->RewSkillValue
= 0;
4223 if (qinfo
->RewSkillValue
)
4225 if (qinfo
->RewSkillValue
> sWorld
.GetConfigMaxSkillValue())
4227 sLog
.outErrorDb("Quest %u has `RewSkillValue` = %u which is more than max possible skill value %u.",
4228 qinfo
->GetQuestId(), qinfo
->RewSkillValue
, sWorld
.GetConfigMaxSkillValue());
4233 if (qinfo
->RewSkill
)
4235 sLog
.outErrorDb("Quest %u has `RewSkillValue` = %u, but `RewSkill` exists and is %u.",
4236 qinfo
->GetQuestId(), qinfo
->RewSkillValue
, qinfo
->RewSkill
);
4237 qinfo
->RewSkill
= 0;
4241 if (qinfo
->ReqSpellLearned
)
4243 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(qinfo
->ReqSpellLearned
);
4247 sLog
.outErrorDb("Quest %u has `ReqSpellLearned` = %u but spell %u does not exist, quest will not have a spell requirement.",
4248 qinfo
->GetQuestId(), qinfo
->ReqSpellLearned
, qinfo
->ReqSpellLearned
);
4249 qinfo
->ReqSpellLearned
= 0;
4251 else if (!SpellMgr::IsSpellValid(spellInfo
))
4253 sLog
.outErrorDb("Quest %u has `ReqSpellLearned` = %u but spell %u is broken, quest will not have a spell requirement.",
4254 qinfo
->GetQuestId(), qinfo
->ReqSpellLearned
, qinfo
->ReqSpellLearned
);
4255 qinfo
->ReqSpellLearned
= 0;
4259 for (int j
= 0; j
< QUEST_REQUIRED_CURRENCY_COUNT
; ++j
)
4261 if (qinfo
->ReqCurrencyId
[j
])
4263 qinfo
->SetSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER
);
4265 CurrencyTypesEntry
const * currencyEntry
= sCurrencyTypesStore
.LookupEntry(qinfo
->ReqCurrencyId
[j
]);
4268 sLog
.outErrorDb("Quest %u has `ReqCurrencyId%d` = %u but currency with entry %u does not exist, quest can not be completed.",
4269 qinfo
->GetQuestId(), j
+ 1, qinfo
->ReqCurrencyId
[j
], qinfo
->ReqCurrencyId
[j
]);
4270 qinfo
->ReqCurrencyId
[j
] = 0;
4271 qinfo
->ReqCurrencyCount
[j
] = 0;
4275 if (!qinfo
->ReqCurrencyCount
[j
])
4277 sLog
.outErrorDb("Quest %u has `ReqCurrencyId%d` = %u but `ReqCurrencyCount%d` = %u.",
4278 qinfo
->GetQuestId(), j
+ 1, qinfo
->ReqCurrencyId
[j
], j
+ 1, qinfo
->ReqCurrencyCount
[j
]);
4279 qinfo
->ReqCurrencyId
[j
] = 0;
4281 else if (currencyEntry
->TotalCap
&& qinfo
->ReqCurrencyCount
[j
] > currencyEntry
->TotalCap
)
4283 sLog
.outErrorDb("Quest %u has `ReqCurrencyCount%d` = %u but currency %u has max count %u.",
4284 qinfo
->GetQuestId(), j
+ 1, qinfo
->ReqCurrencyCount
[j
], qinfo
->ReqCurrencyId
[j
], currencyEntry
->TotalCap
);
4285 qinfo
->ReqCurrencyCount
[j
] = currencyEntry
->TotalCap
;
4289 else if (qinfo
->ReqCurrencyCount
[j
])
4291 if (!qinfo
->ReqCurrencyId
[j
])
4293 sLog
.outErrorDb("Quest %u has `ReqCurrencyId%d` = 0 but `ReqCurrencyCount%d` = %u.",
4294 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->ReqCurrencyCount
[j
]);
4295 qinfo
->ReqCurrencyCount
[j
] = 0;
4300 for (int j
= 0; j
< QUEST_REWARD_CURRENCY_COUNT
; ++j
)
4302 if (qinfo
->RewCurrencyId
[j
])
4304 CurrencyTypesEntry
const * currencyEntry
= sCurrencyTypesStore
.LookupEntry(qinfo
->RewCurrencyId
[j
]);
4307 sLog
.outErrorDb("Quest %u has `RewCurrencyId%d` = %u but currency with entry %u does not exist, quest will not reward that currency.",
4308 qinfo
->GetQuestId(), j
+ 1, qinfo
->RewCurrencyId
[j
], qinfo
->RewCurrencyId
[j
]);
4309 qinfo
->RewCurrencyId
[j
] = 0;
4310 qinfo
->RewCurrencyCount
[j
] = 0;
4312 else if (!qinfo
->RewCurrencyCount
[j
])
4314 sLog
.outErrorDb("Quest %u has `RewCurrencyId%d` = %u but `RewCurrencyCount%d` = %u.",
4315 qinfo
->GetQuestId(), j
+ 1, qinfo
->RewCurrencyId
[j
], j
+ 1, qinfo
->RewCurrencyCount
[j
]);
4316 qinfo
->RewCurrencyId
[j
] = 0;
4319 else if (qinfo
->RewCurrencyCount
[j
])
4321 if (!qinfo
->RewCurrencyId
[j
])
4323 sLog
.outErrorDb("Quest %u has `RewCurrencyId%d` = 0 but `RewCurrencyCount%d` = %u.",
4324 qinfo
->GetQuestId(), j
+ 1, j
+ 1, qinfo
->RewCurrencyCount
[j
]);
4325 qinfo
->RewCurrencyCount
[j
] = 0;
4330 if (qinfo
->SoundAcceptId
)
4332 SoundEntriesEntry
const * soundEntry
= sSoundEntriesStore
.LookupEntry(qinfo
->SoundAcceptId
);
4335 sLog
.outErrorDb("Quest %u has `SoundAcceptId` = %u but sound with that entry does not exists.",
4336 qinfo
->GetQuestId(), qinfo
->SoundAcceptId
);
4337 qinfo
->SoundAcceptId
= 0;
4341 if (qinfo
->SoundTurnInId
)
4343 SoundEntriesEntry
const * soundEntry
= sSoundEntriesStore
.LookupEntry(qinfo
->SoundTurnInId
);
4346 sLog
.outErrorDb("Quest %u has `SoundTurnInId` = %u but sound with that entry does not exists.",
4347 qinfo
->GetQuestId(), qinfo
->SoundTurnInId
);
4348 qinfo
->SoundTurnInId
= 0;
4352 if (qinfo
->ExclusiveGroup
)
4353 m_ExclusiveQuestGroups
.insert(ExclusiveQuestGroupsMap::value_type(qinfo
->ExclusiveGroup
, qinfo
->GetQuestId()));
4355 if (qinfo
->LimitTime
)
4356 qinfo
->SetSpecialFlag(QUEST_SPECIAL_FLAG_TIMED
);
4359 // check QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
4360 for (uint32 i
= 0; i
< sSpellStore
.GetNumRows(); ++i
)
4362 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(i
);
4366 for (int j
= 0; j
< MAX_EFFECT_INDEX
; ++j
)
4368 SpellEffectEntry
const* spellEffect
= spellInfo
->GetSpellEffect(SpellEffectIndex(j
));
4371 if (spellEffect
->Effect
!= SPELL_EFFECT_QUEST_COMPLETE
)
4374 uint32 quest_id
= spellEffect
->EffectMiscValue
;
4376 Quest
const* quest
= GetQuestTemplate(quest_id
);
4378 // some quest referenced in spells not exist (outdated spells)
4382 if (!quest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT
))
4384 sLog
.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest does not have SpecialFlags QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT (2) set. Quest SpecialFlags should be corrected to enable this objective.", spellInfo
->Id
, quest_id
);
4386 // The below forced alteration has been disabled because of spell 33824 / quest 10162.
4387 // A startup error will still occur with proper data in quest_template, but it will be possible to sucessfully complete the quest with the expected data.
4389 // this will prevent quest completing without objective
4390 // const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
4396 sLog
.outString(">> Loaded " SIZEFMTD
" quests definitions", mQuestTemplates
.size());
4399 void ObjectMgr::LoadQuestLocales()
4401 mQuestLocaleMap
.clear(); // need for reload case
4403 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,"
4404 // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
4405 "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,CompletedText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,PortraitGiverName_loc1,PortraitGiverText_loc1,PortraitTurnInName_loc1,PortraitTurnInText_loc1,"
4406 "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,CompletedText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,PortraitGiverName_loc2,PortraitGiverText_loc2,PortraitTurnInName_loc2,PortraitTurnInText_loc2,"
4407 "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,CompletedText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,PortraitGiverName_loc3,PortraitGiverText_loc3,PortraitTurnInName_loc3,PortraitTurnInText_loc3,"
4408 "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,CompletedText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,PortraitGiverName_loc4,PortraitGiverText_loc4,PortraitTurnInName_loc4,PortraitTurnInText_loc4,"
4409 "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,CompletedText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,PortraitGiverName_loc5,PortraitGiverText_loc5,PortraitTurnInName_loc5,PortraitTurnInText_loc5,"
4410 "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,CompletedText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,PortraitGiverName_loc6,PortraitGiverText_loc6,PortraitTurnInName_loc6,PortraitTurnInText_loc6,"
4411 "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,CompletedText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,PortraitGiverName_loc7,PortraitGiverText_loc7,PortraitTurnInName_loc7,PortraitTurnInText_loc7,"
4412 "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,CompletedText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8,PortraitGiverName_loc8,PortraitGiverText_loc8,PortraitTurnInName_loc8,PortraitTurnInText_loc8"
4413 " FROM locales_quest"
4423 sLog
.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
4427 BarGoLink
bar(result
->GetRowCount());
4431 Field
* fields
= result
->Fetch();
4434 uint32 entry
= fields
[0].GetUInt32();
4436 if (!GetQuestTemplate(entry
))
4438 ERROR_DB_STRICT_LOG("Table `locales_quest` has data for nonexistent quest entry %u, skipped.", entry
);
4442 QuestLocale
& data
= mQuestLocaleMap
[entry
];
4444 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
4446 std::string str
= fields
[1 + 15 * (i
- 1)].GetCppString();
4449 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4452 if ((int32
)data
.Title
.size() <= idx
)
4453 data
.Title
.resize(idx
+ 1);
4455 data
.Title
[idx
] = str
;
4458 str
= fields
[1 + 15 * (i
- 1) + 1].GetCppString();
4461 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4464 if ((int32
)data
.Details
.size() <= idx
)
4465 data
.Details
.resize(idx
+ 1);
4467 data
.Details
[idx
] = str
;
4470 str
= fields
[1 + 15 * (i
- 1) + 2].GetCppString();
4473 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4476 if ((int32
)data
.Objectives
.size() <= idx
)
4477 data
.Objectives
.resize(idx
+ 1);
4479 data
.Objectives
[idx
] = str
;
4482 str
= fields
[1 + 15 * (i
- 1) + 3].GetCppString();
4485 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4488 if ((int32
)data
.OfferRewardText
.size() <= idx
)
4489 data
.OfferRewardText
.resize(idx
+ 1);
4491 data
.OfferRewardText
[idx
] = str
;
4494 str
= fields
[1 + 15 * (i
- 1) + 4].GetCppString();
4497 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4500 if ((int32
)data
.RequestItemsText
.size() <= idx
)
4501 data
.RequestItemsText
.resize(idx
+ 1);
4503 data
.RequestItemsText
[idx
] = str
;
4506 str
= fields
[1 + 15 * (i
- 1) + 5].GetCppString();
4509 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4512 if ((int32
)data
.EndText
.size() <= idx
)
4513 data
.EndText
.resize(idx
+ 1);
4515 data
.EndText
[idx
] = str
;
4518 str
= fields
[1 + 15 * (i
- 1) + 6].GetCppString();
4521 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4524 if ((int32
)data
.CompletedText
.size() <= idx
)
4525 data
.CompletedText
.resize(idx
+ 1);
4527 data
.CompletedText
[idx
] = str
;
4530 for (int k
= 0; k
< 4; ++k
)
4532 str
= fields
[1 + 15 * (i
- 1) + 7 + k
].GetCppString();
4535 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4538 if ((int32
)data
.ObjectiveText
[k
].size() <= idx
)
4539 data
.ObjectiveText
[k
].resize(idx
+ 1);
4541 data
.ObjectiveText
[k
][idx
] = str
;
4545 str
= fields
[1 + 15 * (i
- 1) + 11].GetCppString();
4548 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4551 if ((int32
)data
.PortraitGiverName
.size() <= idx
)
4552 data
.PortraitGiverName
.resize(idx
+ 1);
4554 data
.PortraitGiverName
[idx
] = str
;
4557 str
= fields
[1 + 15 * (i
- 1) + 12].GetCppString();
4560 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4563 if ((int32
)data
.PortraitGiverText
.size() <= idx
)
4564 data
.PortraitGiverText
.resize(idx
+ 1);
4566 data
.PortraitGiverText
[idx
] = str
;
4569 str
= fields
[1 + 15 * (i
- 1) + 13].GetCppString();
4572 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4575 if ((int32
)data
.PortraitTurnInName
.size() <= idx
)
4576 data
.PortraitTurnInName
.resize(idx
+ 1);
4578 data
.PortraitTurnInName
[idx
] = str
;
4581 str
= fields
[1 + 15 * (i
- 1) + 14].GetCppString();
4584 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4587 if ((int32
)data
.PortraitTurnInText
.size() <= idx
)
4588 data
.PortraitTurnInText
.resize(idx
+ 1);
4590 data
.PortraitTurnInText
[idx
] = str
;
4595 while (result
->NextRow());
4600 sLog
.outString(">> Loaded " SIZEFMTD
" Quest locale strings", mQuestLocaleMap
.size());
4603 void ObjectMgr::LoadPageTexts()
4605 sPageTextStore
.Load();
4606 sLog
.outString(">> Loaded %u page texts", sPageTextStore
.GetRecordCount());
4609 for (uint32 i
= 1; i
< sPageTextStore
.GetMaxEntry(); ++i
)
4611 // check data correctness
4612 PageText
const* page
= sPageTextStore
.LookupEntry
<PageText
>(i
);
4616 if (page
->Next_Page
&& !sPageTextStore
.LookupEntry
<PageText
>(page
->Next_Page
))
4618 sLog
.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i
, page
->Next_Page
);
4622 // detect circular reference
4623 std::set
<uint32
> checkedPages
;
4624 for (PageText
const* pageItr
= page
; pageItr
; pageItr
= sPageTextStore
.LookupEntry
<PageText
>(pageItr
->Next_Page
))
4626 if (!pageItr
->Next_Page
)
4628 checkedPages
.insert(pageItr
->Page_ID
);
4629 if (checkedPages
.find(pageItr
->Next_Page
) != checkedPages
.end())
4631 std::ostringstream ss
;
4632 ss
<< "The text page(s) ";
4633 for (std::set
<uint32
>::iterator itr
= checkedPages
.begin(); itr
!= checkedPages
.end(); ++itr
)
4635 ss
<< "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
4636 << pageItr
->Page_ID
<< " to 0";
4637 sLog
.outErrorDb("%s", ss
.str().c_str());
4638 const_cast<PageText
*>(pageItr
)->Next_Page
= 0;
4645 void ObjectMgr::LoadPageTextLocales()
4647 mPageTextLocaleMap
.clear(); // need for reload case
4649 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
4658 sLog
.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
4662 BarGoLink
bar(result
->GetRowCount());
4666 Field
* fields
= result
->Fetch();
4669 uint32 entry
= fields
[0].GetUInt32();
4671 if (!sPageTextStore
.LookupEntry
<PageText
>(entry
))
4673 ERROR_DB_STRICT_LOG("Table `locales_page_text` has data for nonexistent page text entry %u, skipped.", entry
);
4677 PageTextLocale
& data
= mPageTextLocaleMap
[entry
];
4679 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
4681 std::string str
= fields
[i
].GetCppString();
4685 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
4688 if ((int32
)data
.Text
.size() <= idx
)
4689 data
.Text
.resize(idx
+ 1);
4691 data
.Text
[idx
] = str
;
4696 while (result
->NextRow());
4701 sLog
.outString(">> Loaded " SIZEFMTD
" PageText locale strings", mPageTextLocaleMap
.size());
4704 void ObjectMgr::LoadInstanceEncounters()
4706 m_DungeonEncounters
.clear(); // need for reload case
4708 QueryResult
* result
= WorldDatabase
.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
4717 sLog
.outString(">> Loaded 0 Instance Encounters. DB table `instance_encounters` is empty.");
4721 BarGoLink
bar(result
->GetRowCount());
4725 Field
* fields
= result
->Fetch();
4728 uint32 entry
= fields
[0].GetUInt32();
4729 DungeonEncounterEntry
const* dungeonEncounter
= sDungeonEncounterStore
.LookupEntry(entry
);
4731 if (!dungeonEncounter
)
4733 sLog
.outErrorDb("Table `instance_encounters` has an invalid encounter id %u, skipped!", entry
);
4737 uint8 creditType
= fields
[1].GetUInt8();
4738 uint32 creditEntry
= fields
[2].GetUInt32();
4741 case ENCOUNTER_CREDIT_KILL_CREATURE
:
4743 CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(creditEntry
);
4746 sLog
.outErrorDb("Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry
, entry
, dungeonEncounter
->encounterName
[0]);
4751 case ENCOUNTER_CREDIT_CAST_SPELL
:
4753 if (!sSpellStore
.LookupEntry(creditEntry
))
4755 // skip spells that aren't in dbc for now
4756 // sLog.outErrorDb("Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName[0]);
4762 sLog
.outErrorDb("Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType
, entry
, dungeonEncounter
->encounterName
[0]);
4765 uint32 lastEncounterDungeon
= fields
[3].GetUInt32();
4767 m_DungeonEncounters
.insert(DungeonEncounterMap::value_type(creditEntry
, new DungeonEncounter(dungeonEncounter
, EncounterCreditType(creditType
), creditEntry
, lastEncounterDungeon
)));
4770 while (result
->NextRow());
4775 sLog
.outString(">> Loaded " SIZEFMTD
" Instance Encounters", m_DungeonEncounters
.size());
4778 struct SQLInstanceLoader
: public SQLStorageLoaderBase
<SQLInstanceLoader
, SQLStorage
>
4781 void convert_from_str(uint32
/*field_pos*/, char const* src
, D
& dst
)
4783 dst
= D(sScriptMgr
.GetScriptId(src
));
4787 void ObjectMgr::LoadInstanceTemplate()
4789 SQLInstanceLoader loader
;
4790 loader
.Load(sInstanceTemplate
);
4792 for (uint32 i
= 0; i
< sInstanceTemplate
.GetMaxEntry(); ++i
)
4794 InstanceTemplate
const* temp
= GetInstanceTemplate(i
);
4798 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(temp
->map
);
4801 sLog
.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp
->map
);
4802 sInstanceTemplate
.EraseEntry(i
);
4806 if (!mapEntry
->Instanceable())
4808 sLog
.outErrorDb("ObjectMgr::LoadInstanceTemplate: non-instanceable mapid %d for template!", temp
->map
);
4809 sInstanceTemplate
.EraseEntry(i
);
4813 if (temp
->parent
> 0)
4816 MapEntry
const* parentEntry
= sMapStore
.LookupEntry(temp
->parent
);
4819 sLog
.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad parent map id %u for instance template %d template!",
4820 temp
->parent
, temp
->map
);
4821 const_cast<InstanceTemplate
*>(temp
)->parent
= 0;
4825 if (parentEntry
->IsContinent())
4827 sLog
.outErrorDb("ObjectMgr::LoadInstanceTemplate: parent point to continent map id %u for instance template %d template, ignored, need be set only for non-continent parents!",
4828 parentEntry
->MapID
, temp
->map
);
4829 const_cast<InstanceTemplate
*>(temp
)->parent
= 0;
4835 sLog
.outString(">> Loaded %u Instance Template definitions", sInstanceTemplate
.GetRecordCount());
4839 struct SQLWorldLoader
: public SQLStorageLoaderBase
<SQLWorldLoader
, SQLStorage
>
4842 void convert_from_str(uint32
/*field_pos*/, char const* src
, D
& dst
)
4844 dst
= D(sScriptMgr
.GetScriptId(src
));
4848 void ObjectMgr::LoadWorldTemplate()
4850 SQLWorldLoader loader
;
4851 loader
.Load(sWorldTemplate
, false);
4853 for (uint32 i
= 0; i
< sWorldTemplate
.GetMaxEntry(); ++i
)
4855 WorldTemplate
const* temp
= GetWorldTemplate(i
);
4859 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(temp
->map
);
4862 sLog
.outErrorDb("ObjectMgr::LoadWorldTemplate: bad mapid %d for template!", temp
->map
);
4863 sWorldTemplate
.EraseEntry(i
);
4867 if (mapEntry
->Instanceable())
4869 sLog
.outErrorDb("ObjectMgr::LoadWorldTemplate: instanceable mapid %d for template!", temp
->map
);
4870 sWorldTemplate
.EraseEntry(i
);
4875 sLog
.outString(">> Loaded %u World Template definitions", sWorldTemplate
.GetRecordCount());
4879 void ObjectMgr::LoadConditions()
4881 SQLWorldLoader loader
;
4882 loader
.Load(sConditionStorage
);
4884 for (uint32 i
= 0; i
< sConditionStorage
.GetMaxEntry(); ++i
)
4886 const PlayerCondition
* condition
= sConditionStorage
.LookupEntry
<PlayerCondition
>(i
);
4890 if (!condition
->IsValid())
4892 sLog
.outErrorDb("ObjectMgr::LoadConditions: invalid condition_entry %u, skip", i
);
4893 sConditionStorage
.EraseEntry(i
);
4898 sLog
.outString(">> Loaded %u Condition definitions", sConditionStorage
.GetRecordCount());
4902 GossipText
const* ObjectMgr::GetGossipText(uint32 Text_ID
) const
4904 GossipTextMap::const_iterator itr
= mGossipText
.find(Text_ID
);
4905 if (itr
!= mGossipText
.end())
4906 return &itr
->second
;
4910 void ObjectMgr::LoadGossipText()
4912 QueryResult
* result
= WorldDatabase
.Query("SELECT * FROM npc_text");
4921 sLog
.outString(">> Loaded %u npc texts", count
);
4927 BarGoLink
bar(result
->GetRowCount());
4934 Field
* fields
= result
->Fetch();
4938 uint32 Text_ID
= fields
[cic
++].GetUInt32();
4941 sLog
.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore.");
4945 GossipText
& gText
= mGossipText
[Text_ID
];
4947 for (int i
= 0; i
< MAX_GOSSIP_TEXT_OPTIONS
; ++i
)
4949 gText
.Options
[i
].Text_0
= fields
[cic
++].GetCppString();
4950 gText
.Options
[i
].Text_1
= fields
[cic
++].GetCppString();
4952 gText
.Options
[i
].Language
= fields
[cic
++].GetUInt32();
4953 gText
.Options
[i
].Probability
= fields
[cic
++].GetFloat();
4955 for (int j
= 0; j
< 3; ++j
)
4957 gText
.Options
[i
].Emotes
[j
]._Delay
= fields
[cic
++].GetUInt32();
4958 gText
.Options
[i
].Emotes
[j
]._Emote
= fields
[cic
++].GetUInt32();
4962 while (result
->NextRow());
4965 sLog
.outString(">> Loaded %u npc texts", count
);
4969 void ObjectMgr::LoadGossipTextLocales()
4971 mNpcTextLocaleMap
.clear(); // need for reload case
4973 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,"
4974 "Text0_0_loc1,Text0_1_loc1,Text1_0_loc1,Text1_1_loc1,Text2_0_loc1,Text2_1_loc1,Text3_0_loc1,Text3_1_loc1,Text4_0_loc1,Text4_1_loc1,Text5_0_loc1,Text5_1_loc1,Text6_0_loc1,Text6_1_loc1,Text7_0_loc1,Text7_1_loc1,"
4975 "Text0_0_loc2,Text0_1_loc2,Text1_0_loc2,Text1_1_loc2,Text2_0_loc2,Text2_1_loc2,Text3_0_loc2,Text3_1_loc1,Text4_0_loc2,Text4_1_loc2,Text5_0_loc2,Text5_1_loc2,Text6_0_loc2,Text6_1_loc2,Text7_0_loc2,Text7_1_loc2,"
4976 "Text0_0_loc3,Text0_1_loc3,Text1_0_loc3,Text1_1_loc3,Text2_0_loc3,Text2_1_loc3,Text3_0_loc3,Text3_1_loc1,Text4_0_loc3,Text4_1_loc3,Text5_0_loc3,Text5_1_loc3,Text6_0_loc3,Text6_1_loc3,Text7_0_loc3,Text7_1_loc3,"
4977 "Text0_0_loc4,Text0_1_loc4,Text1_0_loc4,Text1_1_loc4,Text2_0_loc4,Text2_1_loc4,Text3_0_loc4,Text3_1_loc1,Text4_0_loc4,Text4_1_loc4,Text5_0_loc4,Text5_1_loc4,Text6_0_loc4,Text6_1_loc4,Text7_0_loc4,Text7_1_loc4,"
4978 "Text0_0_loc5,Text0_1_loc5,Text1_0_loc5,Text1_1_loc5,Text2_0_loc5,Text2_1_loc5,Text3_0_loc5,Text3_1_loc1,Text4_0_loc5,Text4_1_loc5,Text5_0_loc5,Text5_1_loc5,Text6_0_loc5,Text6_1_loc5,Text7_0_loc5,Text7_1_loc5,"
4979 "Text0_0_loc6,Text0_1_loc6,Text1_0_loc6,Text1_1_loc6,Text2_0_loc6,Text2_1_loc6,Text3_0_loc6,Text3_1_loc1,Text4_0_loc6,Text4_1_loc6,Text5_0_loc6,Text5_1_loc6,Text6_0_loc6,Text6_1_loc6,Text7_0_loc6,Text7_1_loc6,"
4980 "Text0_0_loc7,Text0_1_loc7,Text1_0_loc7,Text1_1_loc7,Text2_0_loc7,Text2_1_loc7,Text3_0_loc7,Text3_1_loc1,Text4_0_loc7,Text4_1_loc7,Text5_0_loc7,Text5_1_loc7,Text6_0_loc7,Text6_1_loc7,Text7_0_loc7,Text7_1_loc7, "
4981 "Text0_0_loc8,Text0_1_loc8,Text1_0_loc8,Text1_1_loc8,Text2_0_loc8,Text2_1_loc8,Text3_0_loc8,Text3_1_loc1,Text4_0_loc8,Text4_1_loc8,Text5_0_loc8,Text5_1_loc8,Text6_0_loc8,Text6_1_loc8,Text7_0_loc8,Text7_1_loc8 "
4982 " FROM locales_npc_text");
4991 sLog
.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
4995 BarGoLink
bar(result
->GetRowCount());
4999 Field
* fields
= result
->Fetch();
5002 uint32 entry
= fields
[0].GetUInt32();
5004 if (!GetGossipText(entry
))
5006 ERROR_DB_STRICT_LOG("Table `locales_npc_text` has data for nonexistent gossip text entry %u, skipped.", entry
);
5010 NpcTextLocale
& data
= mNpcTextLocaleMap
[entry
];
5012 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
5014 for (int j
= 0; j
< 8; ++j
)
5016 std::string str0
= fields
[1 + 8 * 2 * (i
- 1) + 2 * j
].GetCppString();
5019 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
5022 if ((int32
)data
.Text_0
[j
].size() <= idx
)
5023 data
.Text_0
[j
].resize(idx
+ 1);
5025 data
.Text_0
[j
][idx
] = str0
;
5028 std::string str1
= fields
[1 + 8 * 2 * (i
- 1) + 2 * j
+ 1].GetCppString();
5031 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
5034 if ((int32
)data
.Text_1
[j
].size() <= idx
)
5035 data
.Text_1
[j
].resize(idx
+ 1);
5037 data
.Text_1
[j
][idx
] = str1
;
5043 while (result
->NextRow());
5048 sLog
.outString(">> Loaded " SIZEFMTD
" NpcText locale strings", mNpcTextLocaleMap
.size());
5051 // not very fast function but it is called only once a day, or on starting-up
5052 void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp
)
5054 time_t basetime
= time(NULL
);
5055 DEBUG_LOG("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime
)->tm_hour
, localtime(&basetime
)->tm_min
, localtime(&basetime
)->tm_sec
);
5056 // delete all old mails without item and without body immediately, if starting server
5058 CharacterDatabase
.PExecute("DELETE FROM mail WHERE expire_time < '" UI64FMTD
"' AND has_items = '0' AND body = ''", (uint64
)basetime
);
5059 // 0 1 2 3 4 5 6 7 8
5060 QueryResult
* result
= CharacterDatabase
.PQuery("SELECT id,messageType,sender,receiver,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" UI64FMTD
"'", (uint64
)basetime
);
5066 sLog
.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty.");
5067 return; // any mails need to be returned or deleted
5070 // std::ostringstream delitems, delmails; // will be here for optimization
5071 // bool deletemail = false, deleteitem = false;
5072 // delitems << "DELETE FROM item_instance WHERE guid IN ( ";
5073 // delmails << "DELETE FROM mail WHERE id IN ( "
5075 BarGoLink
bar(result
->GetRowCount());
5083 fields
= result
->Fetch();
5085 m
->messageID
= fields
[0].GetUInt32();
5086 m
->messageType
= fields
[1].GetUInt8();
5087 m
->sender
= fields
[2].GetUInt32();
5088 m
->receiverGuid
= ObjectGuid(HIGHGUID_PLAYER
, fields
[3].GetUInt32());
5089 bool has_items
= fields
[4].GetBool();
5090 m
->expire_time
= (time_t)fields
[5].GetUInt64();
5091 m
->deliver_time
= 0;
5092 m
->COD
= fields
[6].GetUInt32();
5093 m
->checked
= fields
[7].GetUInt32();
5094 m
->mailTemplateId
= fields
[8].GetInt16();
5098 pl
= GetPlayer(m
->receiverGuid
);
5101 // this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
5102 // his in mailbox and he has already listed his mails )
5106 // delete or return mail:
5109 QueryResult
* resultItems
= CharacterDatabase
.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m
->messageID
);
5114 Field
* fields2
= resultItems
->Fetch();
5116 uint32 item_guid_low
= fields2
[0].GetUInt32();
5117 uint32 item_template
= fields2
[1].GetUInt32();
5119 m
->AddItem(item_guid_low
, item_template
);
5121 while (resultItems
->NextRow());
5125 // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
5126 if (m
->messageType
!= MAIL_NORMAL
|| (m
->checked
& (MAIL_CHECK_MASK_COD_PAYMENT
| MAIL_CHECK_MASK_RETURNED
)))
5128 // mail open and then not returned
5129 for (MailItemInfoVec::iterator itr2
= m
->items
.begin(); itr2
!= m
->items
.end(); ++itr2
)
5130 CharacterDatabase
.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2
->item_guid
);
5134 // mail will be returned:
5135 CharacterDatabase
.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" UI64FMTD
"', deliver_time = '" UI64FMTD
"',cod = '0', checked = '%u' WHERE id = '%u'",
5136 m
->receiverGuid
.GetCounter(), m
->sender
, (uint64
)(basetime
+ 30 * DAY
), (uint64
)basetime
, MAIL_CHECK_MASK_RETURNED
, m
->messageID
);
5137 for (MailItemInfoVec::iterator itr2
= m
->items
.begin(); itr2
!= m
->items
.end(); ++itr2
)
5139 // update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
5140 CharacterDatabase
.PExecute("UPDATE mail_items SET receiver = %u WHERE item_guid = '%u'", m
->sender
, itr2
->item_guid
);
5141 CharacterDatabase
.PExecute("UPDATE item_instance SET owner_guid = %u WHERE guid = '%u'", m
->sender
, itr2
->item_guid
);
5148 // deletemail = true;
5149 // delmails << m->messageID << ", ";
5150 CharacterDatabase
.PExecute("DELETE FROM mail WHERE id = '%u'", m
->messageID
);
5154 while (result
->NextRow());
5158 sLog
.outString(">> Loaded %u mails", count
);
5161 void ObjectMgr::LoadQuestAreaTriggers()
5163 mQuestAreaTriggerMap
.clear(); // need for reload case
5165 QueryResult
* result
= WorldDatabase
.Query("SELECT id,quest FROM areatrigger_involvedrelation");
5175 sLog
.outString(">> Loaded %u quest trigger points", count
);
5179 BarGoLink
bar(result
->GetRowCount());
5186 Field
* fields
= result
->Fetch();
5188 uint32 trigger_ID
= fields
[0].GetUInt32();
5189 uint32 quest_ID
= fields
[1].GetUInt32();
5191 AreaTriggerEntry
const* atEntry
= sAreaTriggerStore
.LookupEntry(trigger_ID
);
5194 sLog
.outErrorDb("Table `areatrigger_involvedrelation` has area trigger (ID: %u) not listed in `AreaTrigger.dbc`.", trigger_ID
);
5198 Quest
const* quest
= GetQuestTemplate(quest_ID
);
5201 sLog
.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID
, quest_ID
);
5205 if (!quest
->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT
))
5207 sLog
.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID
, quest_ID
);
5209 // this will prevent quest completing without objective
5210 const_cast<Quest
*>(quest
)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT
);
5212 // continue; - quest modified to required objective and trigger can be allowed.
5215 mQuestAreaTriggerMap
[trigger_ID
] = quest_ID
;
5218 while (result
->NextRow());
5223 sLog
.outString(">> Loaded %u quest trigger points", count
);
5226 void ObjectMgr::LoadTavernAreaTriggers()
5228 mTavernAreaTriggerSet
.clear(); // need for reload case
5230 QueryResult
* result
= WorldDatabase
.Query("SELECT id FROM areatrigger_tavern");
5240 sLog
.outString(">> Loaded %u tavern triggers", count
);
5244 BarGoLink
bar(result
->GetRowCount());
5251 Field
* fields
= result
->Fetch();
5253 uint32 Trigger_ID
= fields
[0].GetUInt32();
5255 AreaTriggerEntry
const* atEntry
= sAreaTriggerStore
.LookupEntry(Trigger_ID
);
5258 sLog
.outErrorDb("Table `areatrigger_tavern` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID
);
5262 mTavernAreaTriggerSet
.insert(Trigger_ID
);
5264 while (result
->NextRow());
5269 sLog
.outString(">> Loaded %u tavern triggers", count
);
5272 uint32
ObjectMgr::GetNearestTaxiNode(float x
, float y
, float z
, uint32 mapid
, Team team
)
5278 for (uint32 i
= 1; i
< sTaxiNodesStore
.GetNumRows(); ++i
)
5280 TaxiNodesEntry
const* node
= sTaxiNodesStore
.LookupEntry(i
);
5281 if (!node
|| node
->map_id
!= mapid
|| !node
->MountCreatureID
[team
== ALLIANCE
? 1 : 0])
5284 uint8 field
= (uint8
)((i
- 1) / 8);
5285 uint8 submask
= 1 << ((i
- 1) % 8);
5287 // skip not taxi network nodes
5288 if ((sTaxiNodesMask
[field
] & submask
) == 0)
5291 float dist2
= (node
->x
- x
) * (node
->x
- x
) + (node
->y
- y
) * (node
->y
- y
) + (node
->z
- z
) * (node
->z
- z
);
5311 void ObjectMgr::GetTaxiPath(uint32 source
, uint32 destination
, uint32
& path
, uint32
& cost
)
5313 TaxiPathSetBySource::iterator src_i
= sTaxiPathSetBySource
.find(source
);
5314 if (src_i
== sTaxiPathSetBySource
.end())
5321 TaxiPathSetForSource
& pathSet
= src_i
->second
;
5323 TaxiPathSetForSource::iterator dest_i
= pathSet
.find(destination
);
5324 if (dest_i
== pathSet
.end())
5331 cost
= dest_i
->second
.price
;
5332 path
= dest_i
->second
.ID
;
5335 uint32
ObjectMgr::GetTaxiMountDisplayId(uint32 id
, Team team
, bool allowed_alt_team
/* = false */)
5337 uint16 mount_entry
= 0;
5339 // select mount creature id
5340 TaxiNodesEntry
const* node
= sTaxiNodesStore
.LookupEntry(id
);
5343 if (team
== ALLIANCE
)
5345 mount_entry
= node
->MountCreatureID
[1];
5346 if (!mount_entry
&& allowed_alt_team
)
5347 mount_entry
= node
->MountCreatureID
[0];
5349 else if (team
== HORDE
)
5351 mount_entry
= node
->MountCreatureID
[0];
5353 if (!mount_entry
&& allowed_alt_team
)
5354 mount_entry
= node
->MountCreatureID
[1];
5358 CreatureInfo
const* mount_info
= GetCreatureTemplate(mount_entry
);
5362 uint16 mount_id
= Creature::ChooseDisplayId(mount_info
);
5366 CreatureModelInfo
const* minfo
= GetCreatureModelRandomGender(mount_id
);
5368 mount_id
= minfo
->modelid
;
5373 void ObjectMgr::LoadGraveyardZones()
5375 mGraveYardMap
.clear(); // need for reload case
5377 QueryResult
* result
= WorldDatabase
.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
5387 sLog
.outString(">> Loaded %u graveyard-zone links", count
);
5391 BarGoLink
bar(result
->GetRowCount());
5398 Field
* fields
= result
->Fetch();
5400 uint32 safeLocId
= fields
[0].GetUInt32();
5401 uint32 zoneId
= fields
[1].GetUInt32();
5402 uint32 team
= fields
[2].GetUInt32();
5404 WorldSafeLocsEntry
const* entry
= sWorldSafeLocsStore
.LookupEntry(safeLocId
);
5407 sLog
.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId
);
5411 AreaTableEntry
const* areaEntry
= GetAreaEntryByAreaID(zoneId
);
5414 sLog
.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.", zoneId
);
5418 if (areaEntry
->zone
!= 0)
5420 sLog
.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.", zoneId
);
5424 if (team
!= TEAM_BOTH_ALLOWED
&& team
!= HORDE
&& team
!= ALLIANCE
)
5426 sLog
.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.", team
);
5430 if (!AddGraveYardLink(safeLocId
, zoneId
, Team(team
), false))
5431 sLog
.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId
, zoneId
);
5433 while (result
->NextRow());
5438 sLog
.outString(">> Loaded %u graveyard-zone links", count
);
5441 WorldSafeLocsEntry
const* ObjectMgr::GetClosestGraveYard(float x
, float y
, float z
, uint32 MapId
, Team team
)
5443 // search for zone associated closest graveyard
5444 uint32 zoneId
= sTerrainMgr
.GetZoneId(MapId
, x
, y
, z
);
5446 // Simulate std. algorithm:
5447 // found some graveyard associated to (ghost_zone,ghost_map)
5449 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
5450 // then check faction
5451 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
5452 // then check faction
5453 GraveYardMapBounds bounds
= mGraveYardMap
.equal_range(zoneId
);
5455 if (bounds
.first
== bounds
.second
)
5457 sLog
.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId
, uint32(team
));
5462 bool foundNear
= false;
5464 WorldSafeLocsEntry
const* entryNear
= NULL
;
5466 // at entrance map for corpse map
5467 bool foundEntr
= false;
5469 WorldSafeLocsEntry
const* entryEntr
= NULL
;
5472 WorldSafeLocsEntry
const* entryFar
= NULL
;
5474 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(MapId
);
5476 for (GraveYardMap::const_iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
5478 GraveYardData
const& data
= itr
->second
;
5481 WorldSafeLocsEntry
const* entry
= sWorldSafeLocsStore
.LookupEntry(data
.safeLocId
);
5483 // skip enemy faction graveyard
5484 // team == TEAM_BOTH_ALLOWED case can be at call from .neargrave
5485 // TEAM_INVALID != team for all teams
5486 if (data
.team
!= TEAM_BOTH_ALLOWED
&& data
.team
!= team
&& team
!= TEAM_BOTH_ALLOWED
)
5489 // find now nearest graveyard at other (continent) map
5490 if (MapId
!= entry
->map_id
)
5492 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
5494 mapEntry
->ghost_entrance_map
< 0 ||
5495 uint32(mapEntry
->ghost_entrance_map
) != entry
->map_id
||
5496 (mapEntry
->ghost_entrance_x
== 0 && mapEntry
->ghost_entrance_y
== 0))
5498 // not have any coordinates for check distance anyway
5503 // at entrance map calculate distance (2D);
5504 float dist2
= (entry
->x
- mapEntry
->ghost_entrance_x
) * (entry
->x
- mapEntry
->ghost_entrance_x
)
5505 + (entry
->y
- mapEntry
->ghost_entrance_y
) * (entry
->y
- mapEntry
->ghost_entrance_y
);
5508 if (dist2
< distEntr
)
5521 // find now nearest graveyard at same map
5524 float dist2
= (entry
->x
- x
) * (entry
->x
- x
) + (entry
->y
- y
) * (entry
->y
- y
) + (entry
->z
- z
) * (entry
->z
- z
);
5527 if (dist2
< distNear
)
5551 GraveYardData
const* ObjectMgr::FindGraveYardData(uint32 id
, uint32 zoneId
) const
5553 GraveYardMapBounds bounds
= mGraveYardMap
.equal_range(zoneId
);
5555 for (GraveYardMap::const_iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
5557 if (itr
->second
.safeLocId
== id
)
5558 return &itr
->second
;
5564 bool ObjectMgr::AddGraveYardLink(uint32 id
, uint32 zoneId
, Team team
, bool inDB
)
5566 if (FindGraveYardData(id
, zoneId
)) // This ensures that (safeLoc)Id, zoneId is unique in mGraveYardMap
5569 // add link to loaded data
5571 data
.safeLocId
= id
;
5574 mGraveYardMap
.insert(GraveYardMap::value_type(zoneId
, data
));
5578 WorldDatabase
.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) VALUES ('%u', '%u','%u')", id
, zoneId
, uint32(team
));
5583 void ObjectMgr::SetGraveYardLinkTeam(uint32 id
, uint32 zoneId
, Team team
)
5585 std::pair
<GraveYardMap::iterator
, GraveYardMap::iterator
> bounds
= mGraveYardMap
.equal_range(zoneId
);
5587 for (GraveYardMap::iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
5589 GraveYardData
& data
= itr
->second
;
5591 // skip not matching safezone id
5592 if (data
.safeLocId
!= id
)
5595 data
.team
= team
; // Validate link
5599 if (team
== TEAM_INVALID
)
5602 // Link expected but not exist.
5603 sLog
.outErrorDb("ObjectMgr::SetGraveYardLinkTeam called for safeLoc %u, zoneId %u, but no graveyard link for this found in database.", id
, zoneId
);
5604 AddGraveYardLink(id
, zoneId
, team
); // Add to prevent further error message and correct mechanismn
5607 void ObjectMgr::LoadAreaTriggerTeleports()
5609 mAreaTriggers
.clear(); // need for reload case
5613 // 0 1 2 3 4 5 6 7 8 9 10 11 12
5614 QueryResult
* result
= WorldDatabase
.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_quest_done_heroic, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
5623 sLog
.outString(">> Loaded %u area trigger teleport definitions", count
);
5627 BarGoLink
bar(result
->GetRowCount());
5631 Field
* fields
= result
->Fetch();
5637 uint32 Trigger_ID
= fields
[0].GetUInt32();
5641 at
.requiredLevel
= fields
[1].GetUInt8();
5642 at
.requiredItem
= fields
[2].GetUInt32();
5643 at
.requiredItem2
= fields
[3].GetUInt32();
5644 at
.heroicKey
= fields
[4].GetUInt32();
5645 at
.heroicKey2
= fields
[5].GetUInt32();
5646 at
.requiredQuest
= fields
[6].GetUInt32();
5647 at
.requiredQuestHeroic
= fields
[7].GetUInt32();
5648 at
.target_mapId
= fields
[8].GetUInt32();
5649 at
.target_X
= fields
[9].GetFloat();
5650 at
.target_Y
= fields
[10].GetFloat();
5651 at
.target_Z
= fields
[11].GetFloat();
5652 at
.target_Orientation
= fields
[12].GetFloat();
5654 AreaTriggerEntry
const* atEntry
= sAreaTriggerStore
.LookupEntry(Trigger_ID
);
5657 sLog
.outErrorDb("Table `areatrigger_teleport` has area trigger (ID:%u) not listed in `AreaTrigger.dbc`.", Trigger_ID
);
5661 if (at
.requiredItem
)
5663 ItemPrototype
const* pProto
= GetItemPrototype(at
.requiredItem
);
5666 sLog
.outError("Table `areatrigger_teleport` has nonexistent key item %u for trigger %u, removing key requirement.", at
.requiredItem
, Trigger_ID
);
5667 at
.requiredItem
= 0;
5671 if (at
.requiredItem2
)
5673 ItemPrototype
const* pProto
= GetItemPrototype(at
.requiredItem2
);
5676 sLog
.outError("Table `areatrigger_teleport` has nonexistent second key item %u for trigger %u, remove key requirement.", at
.requiredItem2
, Trigger_ID
);
5677 at
.requiredItem2
= 0;
5683 ItemPrototype
const* pProto
= GetItemPrototype(at
.heroicKey
);
5686 sLog
.outError("Table `areatrigger_teleport` has nonexistent heroic key item %u for trigger %u, remove key requirement.", at
.heroicKey
, Trigger_ID
);
5693 ItemPrototype
const* pProto
= GetItemPrototype(at
.heroicKey2
);
5696 sLog
.outError("Table `areatrigger_teleport` has nonexistent heroic second key item %u for trigger %u, remove key requirement.", at
.heroicKey2
, Trigger_ID
);
5701 if (at
.requiredQuest
)
5703 QuestMap::iterator qReqItr
= mQuestTemplates
.find(at
.requiredQuest
);
5704 if (qReqItr
== mQuestTemplates
.end())
5706 sLog
.outErrorDb("Table `areatrigger_teleport` has nonexistent required quest %u for trigger %u, remove quest done requirement.", at
.requiredQuest
, Trigger_ID
);
5707 at
.requiredQuest
= 0;
5711 if (at
.requiredQuestHeroic
)
5713 QuestMap::iterator qReqItr
= mQuestTemplates
.find(at
.requiredQuestHeroic
);
5714 if (qReqItr
== mQuestTemplates
.end())
5716 sLog
.outErrorDb("Table `areatrigger_teleport` has nonexistent required heroic quest %u for trigger %u, remove quest done requirement.", at
.requiredQuestHeroic
, Trigger_ID
);
5717 at
.requiredQuestHeroic
= 0;
5721 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(at
.target_mapId
);
5724 sLog
.outErrorDb("Table `areatrigger_teleport` has nonexistent target map (ID: %u) for Area trigger (ID:%u).", at
.target_mapId
, Trigger_ID
);
5728 if (at
.target_X
== 0 && at
.target_Y
== 0 && at
.target_Z
== 0)
5730 sLog
.outErrorDb("Table `areatrigger_teleport` has area trigger (ID:%u) without target coordinates.", Trigger_ID
);
5734 mAreaTriggers
[Trigger_ID
] = at
;
5737 while (result
->NextRow());
5742 sLog
.outString(">> Loaded %u area trigger teleport definitions", count
);
5746 * Searches for the areatrigger which teleports players out of the given map (only direct to continent)
5748 AreaTrigger
const* ObjectMgr::GetGoBackTrigger(uint32 map_id
) const
5750 const MapEntry
* mapEntry
= sMapStore
.LookupEntry(map_id
);
5751 if (!mapEntry
|| mapEntry
->ghost_entrance_map
< 0)
5754 // Try to find one that teleports to the map we want to enter
5755 std::list
<AreaTrigger
const*> ghostTrigger
;
5756 AreaTrigger
const* compareTrigger
= NULL
;
5757 for (AreaTriggerMap::const_iterator itr
= mAreaTriggers
.begin(); itr
!= mAreaTriggers
.end(); ++itr
)
5759 if (itr
->second
.target_mapId
== uint32(mapEntry
->ghost_entrance_map
))
5761 ghostTrigger
.push_back(&itr
->second
);
5762 // First run, only consider AreaTrigger that teleport in the proper map
5763 if ((!compareTrigger
|| itr
->second
.IsLessOrEqualThan(compareTrigger
)) && sAreaTriggerStore
.LookupEntry(itr
->first
)->mapid
== map_id
)
5765 if (itr
->second
.IsMinimal())
5766 return &itr
->second
;
5768 compareTrigger
= &itr
->second
;
5773 return compareTrigger
;
5775 // Second attempt: take one fitting
5776 for (std::list
<AreaTrigger
const*>::const_iterator itr
= ghostTrigger
.begin(); itr
!= ghostTrigger
.end(); ++itr
)
5778 if (!compareTrigger
|| (*itr
)->IsLessOrEqualThan(compareTrigger
))
5780 if ((*itr
)->IsMinimal())
5783 compareTrigger
= *itr
;
5786 return compareTrigger
;
5790 * Searches for the areatrigger which teleports players to the given map
5792 AreaTrigger
const* ObjectMgr::GetMapEntranceTrigger(uint32 Map
) const
5794 AreaTrigger
const* compareTrigger
= NULL
;
5795 MapEntry
const* mEntry
= sMapStore
.LookupEntry(Map
);
5797 for (AreaTriggerMap::const_iterator itr
= mAreaTriggers
.begin(); itr
!= mAreaTriggers
.end(); ++itr
)
5799 if (itr
->second
.target_mapId
== Map
)
5801 if (mEntry
->Instanceable())
5803 // Remark that IsLessOrEqualThan is no total order, and a->IsLeQ(b) != !b->IsLeQ(a)
5804 if (!compareTrigger
|| compareTrigger
->IsLessOrEqualThan(&itr
->second
))
5805 compareTrigger
= &itr
->second
;
5809 if (!compareTrigger
|| itr
->second
.IsLessOrEqualThan(compareTrigger
))
5811 if (itr
->second
.IsMinimal())
5812 return &itr
->second
;
5814 compareTrigger
= &itr
->second
;
5819 return compareTrigger
;
5822 void ObjectMgr::PackGroupIds()
5824 // this routine renumbers groups in such a way so they start from 1 and go up
5826 // obtain set of all groups
5827 std::set
<uint32
> groupIds
;
5829 // all valid ids are in the instance table
5830 // any associations to ids not in this table are assumed to be
5831 // cleaned already in CleanupInstances
5832 QueryResult
* result
= CharacterDatabase
.Query("SELECT groupId FROM groups");
5837 Field
* fields
= result
->Fetch();
5839 uint32 id
= fields
[0].GetUInt32();
5843 CharacterDatabase
.BeginTransaction();
5844 CharacterDatabase
.PExecute("DELETE FROM groups WHERE groupId = '%u'", id
);
5845 CharacterDatabase
.PExecute("DELETE FROM group_member WHERE groupId = '%u'", id
);
5846 CharacterDatabase
.CommitTransaction();
5850 groupIds
.insert(id
);
5852 while (result
->NextRow());
5856 BarGoLink
bar(groupIds
.size() + 1);
5860 // we do assume std::set is sorted properly on integer value
5861 for (std::set
<uint32
>::iterator i
= groupIds
.begin(); i
!= groupIds
.end(); ++i
)
5866 CharacterDatabase
.BeginTransaction();
5867 CharacterDatabase
.PExecute("UPDATE groups SET groupId = '%u' WHERE groupId = '%u'", groupId
, *i
);
5868 CharacterDatabase
.PExecute("UPDATE group_member SET groupId = '%u' WHERE groupId = '%u'", groupId
, *i
);
5869 CharacterDatabase
.CommitTransaction();
5876 sLog
.outString(">> Group Ids remapped, next group id is %u", groupId
);
5880 void ObjectMgr::SetHighestGuids()
5882 QueryResult
* result
= CharacterDatabase
.Query("SELECT MAX(guid) FROM characters");
5885 m_CharGuids
.Set((*result
)[0].GetUInt32() + 1);
5889 result
= WorldDatabase
.Query("SELECT MAX(guid) FROM creature");
5892 m_FirstTemporaryCreatureGuid
= (*result
)[0].GetUInt32() + 1;
5896 result
= CharacterDatabase
.Query("SELECT MAX(guid) FROM item_instance");
5899 m_ItemGuids
.Set((*result
)[0].GetUInt32() + 1);
5903 result
= CharacterDatabase
.Query("SELECT MAX(id) FROM instance");
5906 m_InstanceGuids
.Set((*result
)[0].GetUInt32() + 1);
5910 // Cleanup other tables from nonexistent guids (>=m_hiItemGuid)
5911 CharacterDatabase
.BeginTransaction();
5912 CharacterDatabase
.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_ItemGuids
.GetNextAfterMaxUsed());
5913 CharacterDatabase
.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_ItemGuids
.GetNextAfterMaxUsed());
5914 CharacterDatabase
.PExecute("DELETE FROM auction WHERE itemguid >= '%u'", m_ItemGuids
.GetNextAfterMaxUsed());
5915 CharacterDatabase
.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_ItemGuids
.GetNextAfterMaxUsed());
5916 CharacterDatabase
.CommitTransaction();
5918 result
= WorldDatabase
.Query("SELECT MAX(guid) FROM gameobject");
5921 m_FirstTemporaryGameObjectGuid
= (*result
)[0].GetUInt32() + 1;
5925 result
= CharacterDatabase
.Query("SELECT MAX(id) FROM auction");
5928 m_AuctionIds
.Set((*result
)[0].GetUInt32() + 1);
5932 result
= CharacterDatabase
.Query("SELECT MAX(id) FROM mail");
5935 m_MailIds
.Set((*result
)[0].GetUInt32() + 1);
5939 result
= CharacterDatabase
.Query("SELECT MAX(guid) FROM corpse");
5942 m_CorpseGuids
.Set((*result
)[0].GetUInt32() + 1);
5946 result
= CharacterDatabase
.Query("SELECT MAX(arenateamid) FROM arena_team");
5949 m_ArenaTeamIds
.Set((*result
)[0].GetUInt32() + 1);
5953 result
= CharacterDatabase
.Query("SELECT MAX(setguid) FROM character_equipmentsets");
5956 m_EquipmentSetIds
.Set((*result
)[0].GetUInt64() + 1);
5960 result
= CharacterDatabase
.Query("SELECT MAX(guildid) FROM guild");
5963 m_GuildIds
.Set((*result
)[0].GetUInt32() + 1);
5967 result
= CharacterDatabase
.Query("SELECT MAX(groupId) FROM groups");
5970 m_GroupGuids
.Set((*result
)[0].GetUInt32() + 1);
5974 // setup reserved ranges for static guids spawn
5975 m_StaticCreatureGuids
.Set(m_FirstTemporaryCreatureGuid
);
5976 m_FirstTemporaryCreatureGuid
+= sWorld
.getConfig(CONFIG_UINT32_GUID_RESERVE_SIZE_CREATURE
);
5978 m_StaticGameObjectGuids
.Set(m_FirstTemporaryGameObjectGuid
);
5979 m_FirstTemporaryGameObjectGuid
+= sWorld
.getConfig(CONFIG_UINT32_GUID_RESERVE_SIZE_GAMEOBJECT
);
5982 void ObjectMgr::LoadGameObjectLocales()
5984 mGameObjectLocaleMap
.clear(); // need for reload case
5986 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,"
5987 "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
5988 "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
5989 "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
5998 sLog
.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
6002 BarGoLink
bar(result
->GetRowCount());
6006 Field
* fields
= result
->Fetch();
6009 uint32 entry
= fields
[0].GetUInt32();
6011 if (!GetGameObjectInfo(entry
))
6013 ERROR_DB_STRICT_LOG("Table `locales_gameobject` has data for nonexistent gameobject entry %u, skipped.", entry
);
6017 GameObjectLocale
& data
= mGameObjectLocaleMap
[entry
];
6019 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
6021 std::string str
= fields
[i
].GetCppString();
6024 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
6027 if ((int32
)data
.Name
.size() <= idx
)
6028 data
.Name
.resize(idx
+ 1);
6030 data
.Name
[idx
] = str
;
6035 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
6037 std::string str
= fields
[i
+ (MAX_LOCALE
- 1)].GetCppString();
6040 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
6043 if ((int32
)data
.CastBarCaption
.size() <= idx
)
6044 data
.CastBarCaption
.resize(idx
+ 1);
6046 data
.CastBarCaption
[idx
] = str
;
6052 while (result
->NextRow());
6057 sLog
.outString(">> Loaded " SIZEFMTD
" gameobject locale strings", mGameObjectLocaleMap
.size());
6060 struct SQLGameObjectLoader
: public SQLStorageLoaderBase
<SQLGameObjectLoader
, SQLStorage
>
6063 void convert_from_str(uint32
/*field_pos*/, char const* src
, D
& dst
)
6065 dst
= D(sScriptMgr
.GetScriptId(src
));
6069 inline void CheckGOLockId(GameObjectInfo
const* goInfo
, uint32 dataN
, uint32 N
)
6071 if (sLockStore
.LookupEntry(dataN
))
6074 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
6075 goInfo
->id
, goInfo
->type
, N
, dataN
, dataN
);
6078 inline void CheckGOLinkedTrapId(GameObjectInfo
const* goInfo
, uint32 dataN
, uint32 N
)
6080 if (GameObjectInfo
const* trapInfo
= sGOStorage
.LookupEntry
<GameObjectInfo
>(dataN
))
6082 if (trapInfo
->type
!= GAMEOBJECT_TYPE_TRAP
)
6083 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
6084 goInfo
->id
, goInfo
->type
, N
, dataN
, dataN
, GAMEOBJECT_TYPE_TRAP
);
6087 // too many error reports about nonexistent trap templates
6088 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u GoType: %u) have data%d=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
6089 goInfo
->id
, goInfo
->type
, N
, dataN
, dataN
);
6092 inline void CheckGOSpellId(GameObjectInfo
const* goInfo
, uint32 dataN
, uint32 N
)
6094 if (sSpellStore
.LookupEntry(dataN
))
6097 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
6098 goInfo
->id
, goInfo
->type
, N
, dataN
, dataN
);
6101 inline void CheckAndFixGOChairHeightId(GameObjectInfo
const* goInfo
, uint32
const& dataN
, uint32 N
)
6103 if (dataN
<= (UNIT_STAND_STATE_SIT_HIGH_CHAIR
- UNIT_STAND_STATE_SIT_LOW_CHAIR
))
6106 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
6107 goInfo
->id
, goInfo
->type
, N
, dataN
, UNIT_STAND_STATE_SIT_HIGH_CHAIR
- UNIT_STAND_STATE_SIT_LOW_CHAIR
);
6109 // prevent client and server unexpected work
6110 const_cast<uint32
&>(dataN
) = 0;
6113 inline void CheckGONoDamageImmuneId(GameObjectInfo
const* goInfo
, uint32 dataN
, uint32 N
)
6115 // 0/1 correct values
6119 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.",
6120 goInfo
->id
, goInfo
->type
, N
, dataN
);
6123 inline void CheckGOConsumable(GameObjectInfo
const* goInfo
, uint32 dataN
, uint32 N
)
6125 // 0/1 correct values
6129 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
6130 goInfo
->id
, goInfo
->type
, N
, dataN
);
6133 inline void CheckAndFixGOCaptureMinTime(GameObjectInfo
const* goInfo
, uint32
const& dataN
, uint32 N
)
6138 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) has data%d=%u but minTime field value must be > 0.",
6139 goInfo
->id
, goInfo
->type
, N
, dataN
);
6141 // prevent division through 0 exception
6142 const_cast<uint32
&>(dataN
) = 1;
6145 void ObjectMgr::LoadGameobjectInfo()
6147 SQLGameObjectLoader loader
;
6148 loader
.Load(sGOStorage
);
6151 for (SQLStorageBase::SQLSIterator
<GameObjectInfo
> itr
= sGOStorage
.getDataBegin
<GameObjectInfo
>(); itr
< sGOStorage
.getDataEnd
<GameObjectInfo
>(); ++itr
)
6153 GameObjectInfo
const* goInfo
= itr
.getValue();
6155 if (goInfo
->size
<= 0.0f
) // prevent use too small scales
6157 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u GoType: %u) have too small size=%f",
6158 goInfo
->id
, goInfo
->type
, goInfo
->size
);
6159 const_cast<GameObjectInfo
*>(goInfo
)->size
= DEFAULT_OBJECT_SCALE
;
6162 // some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore
6164 switch (goInfo
->type
)
6166 case GAMEOBJECT_TYPE_DOOR
: // 0
6168 if (goInfo
->door
.lockId
)
6169 CheckGOLockId(goInfo
, goInfo
->door
.lockId
, 1);
6170 CheckGONoDamageImmuneId(goInfo
, goInfo
->door
.noDamageImmune
, 3);
6173 case GAMEOBJECT_TYPE_BUTTON
: // 1
6175 if (goInfo
->button
.lockId
)
6176 CheckGOLockId(goInfo
, goInfo
->button
.lockId
, 1);
6177 if (goInfo
->button
.linkedTrapId
) // linked trap
6178 CheckGOLinkedTrapId(goInfo
, goInfo
->button
.linkedTrapId
, 3);
6179 CheckGONoDamageImmuneId(goInfo
, goInfo
->button
.noDamageImmune
, 4);
6182 case GAMEOBJECT_TYPE_QUESTGIVER
: // 2
6184 if (goInfo
->questgiver
.lockId
)
6185 CheckGOLockId(goInfo
, goInfo
->questgiver
.lockId
, 0);
6186 CheckGONoDamageImmuneId(goInfo
, goInfo
->questgiver
.noDamageImmune
, 5);
6189 case GAMEOBJECT_TYPE_CHEST
: // 3
6191 if (goInfo
->chest
.lockId
)
6192 CheckGOLockId(goInfo
, goInfo
->chest
.lockId
, 0);
6194 CheckGOConsumable(goInfo
, goInfo
->chest
.consumable
, 3);
6196 if (goInfo
->chest
.linkedTrapId
) // linked trap
6197 CheckGOLinkedTrapId(goInfo
, goInfo
->chest
.linkedTrapId
, 7);
6200 case GAMEOBJECT_TYPE_TRAP
: // 6
6202 if (goInfo
->trap
.lockId
)
6203 CheckGOLockId(goInfo
, goInfo
->trap
.lockId
, 0);
6204 /* disable check for while, too many nonexistent spells
6205 if (goInfo->trap.spellId) // spell
6206 CheckGOSpellId(goInfo,goInfo->trap.spellId,3);
6210 case GAMEOBJECT_TYPE_CHAIR
: // 7
6211 CheckAndFixGOChairHeightId(goInfo
, goInfo
->chair
.height
, 1);
6213 case GAMEOBJECT_TYPE_SPELL_FOCUS
: // 8
6215 if (goInfo
->spellFocus
.focusId
)
6217 if (!sSpellFocusObjectStore
.LookupEntry(goInfo
->spellFocus
.focusId
))
6218 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
6219 goInfo
->id
, goInfo
->type
, goInfo
->spellFocus
.focusId
, goInfo
->spellFocus
.focusId
);
6222 if (goInfo
->spellFocus
.linkedTrapId
) // linked trap
6223 CheckGOLinkedTrapId(goInfo
, goInfo
->spellFocus
.linkedTrapId
, 2);
6226 case GAMEOBJECT_TYPE_GOOBER
: // 10
6228 if (goInfo
->goober
.lockId
)
6229 CheckGOLockId(goInfo
, goInfo
->goober
.lockId
, 0);
6231 CheckGOConsumable(goInfo
, goInfo
->goober
.consumable
, 3);
6233 if (goInfo
->goober
.pageId
) // pageId
6235 if (!sPageTextStore
.LookupEntry
<PageText
>(goInfo
->goober
.pageId
))
6236 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
6237 goInfo
->id
, goInfo
->type
, goInfo
->goober
.pageId
, goInfo
->goober
.pageId
);
6239 /* disable check for while, too many nonexistent spells
6240 if (goInfo->goober.spellId) // spell
6241 CheckGOSpellId(goInfo,goInfo->goober.spellId,10);
6243 CheckGONoDamageImmuneId(goInfo
, goInfo
->goober
.noDamageImmune
, 11);
6244 if (goInfo
->goober
.linkedTrapId
) // linked trap
6245 CheckGOLinkedTrapId(goInfo
, goInfo
->goober
.linkedTrapId
, 12);
6248 case GAMEOBJECT_TYPE_AREADAMAGE
: // 12
6250 if (goInfo
->areadamage
.lockId
)
6251 CheckGOLockId(goInfo
, goInfo
->areadamage
.lockId
, 0);
6254 case GAMEOBJECT_TYPE_CAMERA
: // 13
6256 if (goInfo
->camera
.lockId
)
6257 CheckGOLockId(goInfo
, goInfo
->camera
.lockId
, 0);
6260 case GAMEOBJECT_TYPE_MO_TRANSPORT
: // 15
6262 if (goInfo
->moTransport
.taxiPathId
)
6264 if (goInfo
->moTransport
.taxiPathId
>= sTaxiPathNodesByPath
.size() || sTaxiPathNodesByPath
[goInfo
->moTransport
.taxiPathId
].empty())
6265 sLog
.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
6266 goInfo
->id
, goInfo
->type
, goInfo
->moTransport
.taxiPathId
, goInfo
->moTransport
.taxiPathId
);
6270 case GAMEOBJECT_TYPE_SUMMONING_RITUAL
: // 18
6272 /* disable check for while, too many nonexistent spells
6273 // always must have spell
6274 CheckGOSpellId(goInfo,goInfo->summoningRitual.spellId,1);
6278 case GAMEOBJECT_TYPE_SPELLCASTER
: // 22
6280 // always must have spell
6281 CheckGOSpellId(goInfo
, goInfo
->spellcaster
.spellId
, 0);
6284 case GAMEOBJECT_TYPE_FLAGSTAND
: // 24
6286 if (goInfo
->flagstand
.lockId
)
6287 CheckGOLockId(goInfo
, goInfo
->flagstand
.lockId
, 0);
6288 CheckGONoDamageImmuneId(goInfo
, goInfo
->flagstand
.noDamageImmune
, 5);
6291 case GAMEOBJECT_TYPE_FISHINGHOLE
: // 25
6293 if (goInfo
->fishinghole
.lockId
)
6294 CheckGOLockId(goInfo
, goInfo
->fishinghole
.lockId
, 4);
6297 case GAMEOBJECT_TYPE_FLAGDROP
: // 26
6299 if (goInfo
->flagdrop
.lockId
)
6300 CheckGOLockId(goInfo
, goInfo
->flagdrop
.lockId
, 0);
6301 CheckGONoDamageImmuneId(goInfo
, goInfo
->flagdrop
.noDamageImmune
, 3);
6304 case GAMEOBJECT_TYPE_CAPTURE_POINT
: // 29
6306 CheckAndFixGOCaptureMinTime(goInfo
, goInfo
->capturePoint
.minTime
, 16);
6309 case GAMEOBJECT_TYPE_BARBER_CHAIR
: // 32
6310 CheckAndFixGOChairHeightId(goInfo
, goInfo
->barberChair
.chairheight
, 0);
6315 sLog
.outString(">> Loaded %u game object templates", sGOStorage
.GetRecordCount());
6319 void ObjectMgr::LoadExplorationBaseXP()
6322 QueryResult
* result
= WorldDatabase
.Query("SELECT level,basexp FROM exploration_basexp");
6331 sLog
.outString(">> Loaded %u BaseXP definitions", count
);
6335 BarGoLink
bar(result
->GetRowCount());
6341 Field
* fields
= result
->Fetch();
6342 uint32 level
= fields
[0].GetUInt32();
6343 uint32 basexp
= fields
[1].GetUInt32();
6344 mBaseXPTable
[level
] = basexp
;
6347 while (result
->NextRow());
6352 sLog
.outString(">> Loaded %u BaseXP definitions", count
);
6355 uint32
ObjectMgr::GetBaseXP(uint32 level
) const
6357 BaseXPMap::const_iterator itr
= mBaseXPTable
.find(level
);
6358 return itr
!= mBaseXPTable
.end() ? itr
->second
: 0;
6361 uint32
ObjectMgr::GetXPForLevel(uint32 level
) const
6363 if (level
< mPlayerXPperLevel
.size())
6364 return mPlayerXPperLevel
[level
];
6368 void ObjectMgr::LoadPetNames()
6371 QueryResult
* result
= WorldDatabase
.Query("SELECT word,entry,half FROM pet_name_generation");
6380 sLog
.outString(">> Loaded %u pet name parts", count
);
6384 BarGoLink
bar(result
->GetRowCount());
6390 Field
* fields
= result
->Fetch();
6391 std::string word
= fields
[0].GetString();
6392 uint32 entry
= fields
[1].GetUInt32();
6393 bool half
= fields
[2].GetBool();
6395 PetHalfName1
[entry
].push_back(word
);
6397 PetHalfName0
[entry
].push_back(word
);
6400 while (result
->NextRow());
6404 sLog
.outString(">> Loaded %u pet name parts", count
);
6407 void ObjectMgr::LoadPetNumber()
6409 QueryResult
* result
= CharacterDatabase
.Query("SELECT MAX(id) FROM character_pet");
6412 Field
* fields
= result
->Fetch();
6413 m_PetNumbers
.Set(fields
[0].GetUInt32() + 1);
6421 sLog
.outString(">> Loaded the max pet number: %d", m_PetNumbers
.GetNextAfterMaxUsed() - 1);
6424 std::string
ObjectMgr::GeneratePetName(uint32 entry
)
6426 std::vector
<std::string
>& list0
= PetHalfName0
[entry
];
6427 std::vector
<std::string
>& list1
= PetHalfName1
[entry
];
6429 if (list0
.empty() || list1
.empty())
6431 CreatureInfo
const* cinfo
= GetCreatureTemplate(entry
);
6432 char const* petname
= GetPetName(cinfo
->family
, sWorld
.GetDefaultDbcLocale());
6434 petname
= cinfo
->Name
;
6435 return std::string(petname
);
6438 return *(list0
.begin() + urand(0, list0
.size() - 1)) + *(list1
.begin() + urand(0, list1
.size() - 1));
6441 void ObjectMgr::LoadCorpses()
6445 QueryResult
* result
= CharacterDatabase
.Query("SELECT corpse.guid, player, corpse.position_x, corpse.position_y, corpse.position_z, corpse.orientation, corpse.map, "
6446 // 7 8 9 10 11 12 13 14 15 16 17 18
6447 "time, corpse_type, instance, phaseMask, gender, race, class, playerBytes, playerBytes2, equipmentCache, guildId, playerFlags FROM corpse "
6448 "JOIN characters ON player = characters.guid "
6449 "LEFT JOIN guild_member ON player=guild_member.guid WHERE corpse_type <> 0");
6458 sLog
.outString(">> Loaded %u corpses", count
);
6462 BarGoLink
bar(result
->GetRowCount());
6468 Field
* fields
= result
->Fetch();
6470 uint32 guid
= fields
[0].GetUInt32();
6472 Corpse
* corpse
= new Corpse
;
6473 if (!corpse
->LoadFromDB(guid
, fields
))
6479 sObjectAccessor
.AddCorpse(corpse
);
6483 while (result
->NextRow());
6487 sLog
.outString(">> Loaded %u corpses", count
);
6490 void ObjectMgr::LoadReputationRewardRate()
6492 m_RepRewardRateMap
.clear(); // for reload case
6495 QueryResult
* result
= WorldDatabase
.Query("SELECT faction, quest_rate, creature_rate, spell_rate FROM reputation_reward_rate");
6504 sLog
.outErrorDb(">> Loaded `reputation_reward_rate`, table is empty!");
6508 BarGoLink
bar(result
->GetRowCount());
6514 Field
* fields
= result
->Fetch();
6516 uint32 factionId
= fields
[0].GetUInt32();
6518 RepRewardRate repRate
;
6520 repRate
.quest_rate
= fields
[1].GetFloat();
6521 repRate
.creature_rate
= fields
[2].GetFloat();
6522 repRate
.spell_rate
= fields
[3].GetFloat();
6524 FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(factionId
);
6527 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId
);
6531 if (repRate
.quest_rate
< 0.0f
)
6533 sLog
.outErrorDb("Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate
.quest_rate
, factionId
);
6537 if (repRate
.creature_rate
< 0.0f
)
6539 sLog
.outErrorDb("Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate
.creature_rate
, factionId
);
6543 if (repRate
.spell_rate
< 0.0f
)
6545 sLog
.outErrorDb("Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate
.spell_rate
, factionId
);
6549 m_RepRewardRateMap
[factionId
] = repRate
;
6553 while (result
->NextRow());
6558 sLog
.outString(">> Loaded %u reputation_reward_rate", count
);
6561 void ObjectMgr::LoadReputationOnKill()
6566 QueryResult
* result
= WorldDatabase
.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
6568 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
6569 "FROM creature_onkill_reputation");
6578 sLog
.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
6582 BarGoLink
bar(result
->GetRowCount());
6586 Field
* fields
= result
->Fetch();
6589 uint32 creature_id
= fields
[0].GetUInt32();
6591 ReputationOnKillEntry repOnKill
;
6592 repOnKill
.repfaction1
= fields
[1].GetUInt32();
6593 repOnKill
.repfaction2
= fields
[2].GetUInt32();
6594 repOnKill
.is_teamaward1
= fields
[3].GetBool();
6595 repOnKill
.reputation_max_cap1
= fields
[4].GetUInt32();
6596 repOnKill
.repvalue1
= fields
[5].GetInt32();
6597 repOnKill
.is_teamaward2
= fields
[6].GetBool();
6598 repOnKill
.reputation_max_cap2
= fields
[7].GetUInt32();
6599 repOnKill
.repvalue2
= fields
[8].GetInt32();
6600 repOnKill
.team_dependent
= fields
[9].GetUInt8();
6602 if (!GetCreatureTemplate(creature_id
))
6604 sLog
.outErrorDb("Table `creature_onkill_reputation` have data for nonexistent creature entry (%u), skipped", creature_id
);
6608 if (repOnKill
.repfaction1
)
6610 FactionEntry
const* factionEntry1
= sFactionStore
.LookupEntry(repOnKill
.repfaction1
);
6613 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill
.repfaction1
);
6618 if (repOnKill
.repfaction2
)
6620 FactionEntry
const* factionEntry2
= sFactionStore
.LookupEntry(repOnKill
.repfaction2
);
6623 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`", repOnKill
.repfaction2
);
6628 mRepOnKill
[creature_id
] = repOnKill
;
6632 while (result
->NextRow());
6637 sLog
.outString(">> Loaded %u creature award reputation definitions", count
);
6640 void ObjectMgr::LoadReputationSpilloverTemplate()
6642 m_RepSpilloverTemplateMap
.clear(); // for reload case
6645 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");
6654 sLog
.outString(">> Loaded `reputation_spillover_template`, table is empty.");
6658 BarGoLink
bar(result
->GetRowCount());
6664 Field
* fields
= result
->Fetch();
6666 uint32 factionId
= fields
[0].GetUInt32();
6668 RepSpilloverTemplate repTemplate
;
6670 repTemplate
.faction
[0] = fields
[1].GetUInt32();
6671 repTemplate
.faction_rate
[0] = fields
[2].GetFloat();
6672 repTemplate
.faction_rank
[0] = fields
[3].GetUInt32();
6673 repTemplate
.faction
[1] = fields
[4].GetUInt32();
6674 repTemplate
.faction_rate
[1] = fields
[5].GetFloat();
6675 repTemplate
.faction_rank
[1] = fields
[6].GetUInt32();
6676 repTemplate
.faction
[2] = fields
[7].GetUInt32();
6677 repTemplate
.faction_rate
[2] = fields
[8].GetFloat();
6678 repTemplate
.faction_rank
[2] = fields
[9].GetUInt32();
6679 repTemplate
.faction
[3] = fields
[10].GetUInt32();
6680 repTemplate
.faction_rate
[3] = fields
[11].GetFloat();
6681 repTemplate
.faction_rank
[3] = fields
[12].GetUInt32();
6683 FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(factionId
);
6687 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId
);
6691 if (factionEntry
->team
== 0)
6693 sLog
.outErrorDb("Faction (faction.dbc) %u in `reputation_spillover_template` does not belong to any team, skipping", factionId
);
6697 for (uint32 i
= 0; i
< MAX_SPILLOVER_FACTIONS
; ++i
)
6699 if (repTemplate
.faction
[i
])
6701 FactionEntry
const* factionSpillover
= sFactionStore
.LookupEntry(repTemplate
.faction
[i
]);
6703 if (!factionSpillover
)
6705 sLog
.outErrorDb("Spillover faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template` for faction %u, skipping", repTemplate
.faction
[i
], factionId
);
6709 if (factionSpillover
->reputationListID
< 0)
6711 sLog
.outErrorDb("Spillover faction (faction.dbc) %u for faction %u in `reputation_spillover_template` can not be listed for client, and then useless, skipping", repTemplate
.faction
[i
], factionId
);
6715 if (repTemplate
.faction_rank
[i
] >= MAX_REPUTATION_RANK
)
6717 sLog
.outErrorDb("Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate
.faction_rank
[i
], repTemplate
.faction
[i
]);
6723 FactionEntry
const* factionEntry0
= sFactionStore
.LookupEntry(repTemplate
.faction
[0]);
6724 if (repTemplate
.faction
[0] && !factionEntry0
)
6726 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate
.faction
[0]);
6729 FactionEntry
const* factionEntry1
= sFactionStore
.LookupEntry(repTemplate
.faction
[1]);
6730 if (repTemplate
.faction
[1] && !factionEntry1
)
6732 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate
.faction
[1]);
6735 FactionEntry
const* factionEntry2
= sFactionStore
.LookupEntry(repTemplate
.faction
[2]);
6736 if (repTemplate
.faction
[2] && !factionEntry2
)
6738 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate
.faction
[2]);
6741 FactionEntry
const* factionEntry3
= sFactionStore
.LookupEntry(repTemplate
.faction
[3]);
6742 if (repTemplate
.faction
[3] && !factionEntry3
)
6744 sLog
.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate
.faction
[3]);
6748 m_RepSpilloverTemplateMap
[factionId
] = repTemplate
;
6752 while (result
->NextRow());
6757 sLog
.outString(">> Loaded %u reputation_spillover_template", count
);
6760 void ObjectMgr::LoadPointsOfInterest()
6762 mPointsOfInterest
.clear(); // need for reload case
6767 QueryResult
* result
= WorldDatabase
.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
6776 sLog
.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
6780 BarGoLink
bar(result
->GetRowCount());
6784 Field
* fields
= result
->Fetch();
6787 uint32 point_id
= fields
[0].GetUInt32();
6789 PointOfInterest POI
;
6790 POI
.x
= fields
[1].GetFloat();
6791 POI
.y
= fields
[2].GetFloat();
6792 POI
.icon
= fields
[3].GetUInt32();
6793 POI
.flags
= fields
[4].GetUInt32();
6794 POI
.data
= fields
[5].GetUInt32();
6795 POI
.icon_name
= fields
[6].GetCppString();
6797 if (!MaNGOS::IsValidMapCoord(POI
.x
, POI
.y
))
6799 sLog
.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.", point_id
, POI
.x
, POI
.y
);
6803 mPointsOfInterest
[point_id
] = POI
;
6807 while (result
->NextRow());
6812 sLog
.outString(">> Loaded %u Points of Interest definitions", count
);
6815 void ObjectMgr::LoadQuestPOI()
6817 mQuestPOIMap
.clear(); // need for reload case
6822 QueryResult
* result
= WorldDatabase
.Query("SELECT questId, poiId, objIndex, mapId, mapAreaId, floorId, unk3, unk4 FROM quest_poi");
6831 sLog
.outErrorDb(">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
6835 BarGoLink
bar(result
->GetRowCount());
6839 Field
* fields
= result
->Fetch();
6842 uint32 questId
= fields
[0].GetUInt32();
6843 uint32 poiId
= fields
[1].GetUInt32();
6844 int32 objIndex
= fields
[2].GetInt32();
6845 uint32 mapId
= fields
[3].GetUInt32();
6846 uint32 mapAreaId
= fields
[4].GetUInt32();
6847 uint32 floorId
= fields
[5].GetUInt32();
6848 uint32 unk3
= fields
[6].GetUInt32();
6849 uint32 unk4
= fields
[7].GetUInt32();
6851 QuestPOI
POI(poiId
, objIndex
, mapId
, mapAreaId
, floorId
, unk3
, unk4
);
6853 mQuestPOIMap
[questId
].push_back(POI
);
6857 while (result
->NextRow());
6861 QueryResult
* points
= WorldDatabase
.Query("SELECT questId, poiId, x, y FROM quest_poi_points");
6867 Field
* pointFields
= points
->Fetch();
6869 uint32 questId
= pointFields
[0].GetUInt32();
6870 uint32 poiId
= pointFields
[1].GetUInt32();
6871 int32 x
= pointFields
[2].GetInt32();
6872 int32 y
= pointFields
[3].GetInt32();
6874 QuestPOIVector
& vect
= mQuestPOIMap
[questId
];
6876 for (QuestPOIVector::iterator itr
= vect
.begin(); itr
!= vect
.end(); ++itr
)
6878 if (itr
->PoiId
!= poiId
)
6881 QuestPOIPoint
point(x
, y
);
6882 itr
->points
.push_back(point
);
6886 while (points
->NextRow());
6892 sLog
.outString(">> Loaded %u quest POI definitions", count
);
6895 void ObjectMgr::LoadQuestPhaseMaps()
6897 mQuestPhaseMap
.clear(); // need for reload case
6902 QueryResult
*result
= WorldDatabase
.Query("SELECT questId, map, phase FROM quest_phase_maps");
6911 sLog
.outErrorDb(">> Loaded 0 quest phase maps definitions. DB table `quest_phase_maps` is empty.");
6915 BarGoLink
bar(result
->GetRowCount());
6919 Field
*fields
= result
->Fetch();
6922 uint32 questId
= fields
[0].GetUInt32();
6923 uint16 mapId
= fields
[1].GetUInt16();
6924 uint32 phase
= fields
[2].GetUInt32();
6926 QuestPhaseMaps
QuestPhase(mapId
, phase
);
6928 mQuestPhaseMap
[questId
].push_back(QuestPhase
);
6931 } while (result
->NextRow());
6936 sLog
.outString(">> Loaded %u quest phase maps definitions", count
);
6939 void ObjectMgr::LoadNPCSpellClickSpells()
6943 mSpellClickInfoMap
.clear();
6945 QueryResult
* result
= WorldDatabase
.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags FROM npc_spellclick_spells");
6954 sLog
.outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
6958 BarGoLink
bar(result
->GetRowCount());
6962 Field
* fields
= result
->Fetch();
6965 uint32 npc_entry
= fields
[0].GetUInt32();
6966 CreatureInfo
const* cInfo
= GetCreatureTemplate(npc_entry
);
6969 sLog
.outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry
);
6973 uint32 spellid
= fields
[1].GetUInt32();
6974 SpellEntry
const* spellinfo
= sSpellStore
.LookupEntry(spellid
);
6977 sLog
.outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid
);
6981 uint32 quest_start
= fields
[2].GetUInt32();
6983 // quest might be 0 to enable spellclick independent of any quest
6986 if (mQuestTemplates
.find(quest_start
) == mQuestTemplates
.end())
6988 sLog
.outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start
);
6994 bool quest_start_active
= fields
[3].GetBool();
6996 uint32 quest_end
= fields
[4].GetUInt32();
6997 // quest might be 0 to enable spellclick active infinity after start quest
7000 if (mQuestTemplates
.find(quest_end
) == mQuestTemplates
.end())
7002 sLog
.outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end
);
7008 uint8 castFlags
= fields
[5].GetUInt8();
7009 SpellClickInfo info
;
7010 info
.spellId
= spellid
;
7011 info
.questStart
= quest_start
;
7012 info
.questStartCanActive
= quest_start_active
;
7013 info
.questEnd
= quest_end
;
7014 info
.castFlags
= castFlags
;
7015 mSpellClickInfoMap
.insert(SpellClickInfoMap::value_type(npc_entry
, info
));
7017 // mark creature template as spell clickable
7018 const_cast<CreatureInfo
*>(cInfo
)->npcflag
|= UNIT_NPC_FLAG_SPELLCLICK
;
7022 while (result
->NextRow());
7027 sLog
.outString(">> Loaded %u spellclick definitions", count
);
7030 void ObjectMgr::LoadWeatherZoneChances()
7034 // 0 1 2 3 4 5 6 7 8 9 10 11 12
7035 QueryResult
* result
= WorldDatabase
.Query("SELECT zone, spring_rain_chance, spring_snow_chance, spring_storm_chance, summer_rain_chance, summer_snow_chance, summer_storm_chance, fall_rain_chance, fall_snow_chance, fall_storm_chance, winter_rain_chance, winter_snow_chance, winter_storm_chance FROM game_weather");
7044 sLog
.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
7048 BarGoLink
bar(result
->GetRowCount());
7052 Field
* fields
= result
->Fetch();
7055 uint32 zone_id
= fields
[0].GetUInt32();
7057 WeatherZoneChances
& wzc
= mWeatherZoneMap
[zone_id
];
7059 for (int season
= 0; season
< WEATHER_SEASONS
; ++season
)
7061 wzc
.data
[season
].rainChance
= fields
[season
* (MAX_WEATHER_TYPE
- 1) + 1].GetUInt32();
7062 wzc
.data
[season
].snowChance
= fields
[season
* (MAX_WEATHER_TYPE
- 1) + 2].GetUInt32();
7063 wzc
.data
[season
].stormChance
= fields
[season
* (MAX_WEATHER_TYPE
- 1) + 3].GetUInt32();
7065 if (wzc
.data
[season
].rainChance
> 100)
7067 wzc
.data
[season
].rainChance
= 25;
7068 sLog
.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%%", zone_id
, season
);
7071 if (wzc
.data
[season
].snowChance
> 100)
7073 wzc
.data
[season
].snowChance
= 25;
7074 sLog
.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%%", zone_id
, season
);
7077 if (wzc
.data
[season
].stormChance
> 100)
7079 wzc
.data
[season
].stormChance
= 25;
7080 sLog
.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%%", zone_id
, season
);
7086 while (result
->NextRow());
7091 sLog
.outString(">> Loaded %u weather definitions", count
);
7094 void ObjectMgr::DeleteCreatureData(uint32 guid
)
7096 // remove mapid*cellid -> guid_set map
7097 CreatureData
const* data
= GetCreatureData(guid
);
7099 RemoveCreatureFromGrid(guid
, data
);
7101 mCreatureDataMap
.erase(guid
);
7104 void ObjectMgr::DeleteGOData(uint32 guid
)
7106 // remove mapid*cellid -> guid_set map
7107 GameObjectData
const* data
= GetGOData(guid
);
7109 RemoveGameobjectFromGrid(guid
, data
);
7111 mGameObjectDataMap
.erase(guid
);
7114 void ObjectMgr::AddCorpseCellData(uint32 mapid
, uint32 cellid
, uint32 player_guid
, uint32 instance
)
7116 // corpses are always added to spawn mode 0 and they are spawned by their instance id
7117 CellObjectGuids
& cell_guids
= mMapObjectGuids
[MAKE_PAIR32(mapid
, 0)][cellid
];
7118 cell_guids
.corpses
[player_guid
] = instance
;
7121 void ObjectMgr::DeleteCorpseCellData(uint32 mapid
, uint32 cellid
, uint32 player_guid
)
7123 // corpses are always added to spawn mode 0 and they are spawned by their instance id
7124 CellObjectGuids
& cell_guids
= mMapObjectGuids
[MAKE_PAIR32(mapid
, 0)][cellid
];
7125 cell_guids
.corpses
.erase(player_guid
);
7128 void ObjectMgr::LoadQuestRelationsHelper(QuestRelationsMap
& map
, char const* table
)
7130 map
.clear(); // need for reload case
7134 QueryResult
* result
= WorldDatabase
.PQuery("SELECT id,quest FROM %s", table
);
7143 sLog
.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.", table
, table
);
7147 BarGoLink
bar(result
->GetRowCount());
7151 Field
* fields
= result
->Fetch();
7154 uint32 id
= fields
[0].GetUInt32();
7155 uint32 quest
= fields
[1].GetUInt32();
7157 if (mQuestTemplates
.find(quest
) == mQuestTemplates
.end())
7159 sLog
.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.", table
, quest
, id
);
7163 map
.insert(QuestRelationsMap::value_type(id
, quest
));
7167 while (result
->NextRow());
7172 sLog
.outString(">> Loaded %u quest relations from %s", count
, table
);
7175 void ObjectMgr::LoadGameobjectQuestRelations()
7177 LoadQuestRelationsHelper(m_GOQuestRelations
, "gameobject_questrelation");
7179 for (QuestRelationsMap::iterator itr
= m_GOQuestRelations
.begin(); itr
!= m_GOQuestRelations
.end(); ++itr
)
7181 GameObjectInfo
const* goInfo
= GetGameObjectInfo(itr
->first
);
7183 sLog
.outErrorDb("Table `gameobject_questrelation` have data for nonexistent gameobject entry (%u) and existing quest %u", itr
->first
, itr
->second
);
7184 else if (goInfo
->type
!= GAMEOBJECT_TYPE_QUESTGIVER
)
7185 sLog
.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr
->first
, itr
->second
);
7189 void ObjectMgr::LoadGameobjectInvolvedRelations()
7191 LoadQuestRelationsHelper(m_GOQuestInvolvedRelations
, "gameobject_involvedrelation");
7193 for (QuestRelationsMap::iterator itr
= m_GOQuestInvolvedRelations
.begin(); itr
!= m_GOQuestInvolvedRelations
.end(); ++itr
)
7195 GameObjectInfo
const* goInfo
= GetGameObjectInfo(itr
->first
);
7197 sLog
.outErrorDb("Table `gameobject_involvedrelation` have data for nonexistent gameobject entry (%u) and existing quest %u", itr
->first
, itr
->second
);
7198 else if (goInfo
->type
!= GAMEOBJECT_TYPE_QUESTGIVER
)
7199 sLog
.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr
->first
, itr
->second
);
7203 void ObjectMgr::LoadCreatureQuestRelations()
7205 LoadQuestRelationsHelper(m_CreatureQuestRelations
, "creature_questrelation");
7207 for (QuestRelationsMap::iterator itr
= m_CreatureQuestRelations
.begin(); itr
!= m_CreatureQuestRelations
.end(); ++itr
)
7209 CreatureInfo
const* cInfo
= GetCreatureTemplate(itr
->first
);
7211 sLog
.outErrorDb("Table `creature_questrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr
->first
, itr
->second
);
7212 else if (!(cInfo
->npcflag
& UNIT_NPC_FLAG_QUESTGIVER
))
7213 sLog
.outErrorDb("Table `creature_questrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr
->first
, itr
->second
);
7217 void ObjectMgr::LoadCreatureInvolvedRelations()
7219 LoadQuestRelationsHelper(m_CreatureQuestInvolvedRelations
, "creature_involvedrelation");
7221 for (QuestRelationsMap::iterator itr
= m_CreatureQuestInvolvedRelations
.begin(); itr
!= m_CreatureQuestInvolvedRelations
.end(); ++itr
)
7223 CreatureInfo
const* cInfo
= GetCreatureTemplate(itr
->first
);
7225 sLog
.outErrorDb("Table `creature_involvedrelation` have data for nonexistent creature entry (%u) and existing quest %u", itr
->first
, itr
->second
);
7226 else if (!(cInfo
->npcflag
& UNIT_NPC_FLAG_QUESTGIVER
))
7227 sLog
.outErrorDb("Table `creature_involvedrelation` has creature entry (%u) for quest %u, but npcflag does not include UNIT_NPC_FLAG_QUESTGIVER", itr
->first
, itr
->second
);
7231 void ObjectMgr::LoadReservedPlayersNames()
7233 m_ReservedNames
.clear(); // need for reload case
7235 QueryResult
* result
= WorldDatabase
.Query("SELECT name FROM reserved_name");
7245 sLog
.outString(">> Loaded %u reserved player names", count
);
7249 BarGoLink
bar(result
->GetRowCount());
7255 fields
= result
->Fetch();
7256 std::string name
= fields
[0].GetCppString();
7259 if (!Utf8toWStr(name
, wstr
))
7261 sLog
.outError("Table `reserved_name` have invalid name: %s", name
.c_str());
7267 m_ReservedNames
.insert(wstr
);
7270 while (result
->NextRow());
7275 sLog
.outString(">> Loaded %u reserved player names", count
);
7278 bool ObjectMgr::IsReservedName(const std::string
& name
) const
7281 if (!Utf8toWStr(name
, wstr
))
7286 return m_ReservedNames
.find(wstr
) != m_ReservedNames
.end();
7291 LT_BASIC_LATIN
= 0x0000,
7292 LT_EXTENDEN_LATIN
= 0x0001,
7293 LT_CYRILLIC
= 0x0002,
7294 LT_EAST_ASIA
= 0x0004,
7298 static LanguageType
GetRealmLanguageType(bool create
)
7300 switch (sWorld
.getConfig(CONFIG_UINT32_REALM_ZONE
))
7302 case REALM_ZONE_UNKNOWN
: // any language
7303 case REALM_ZONE_DEVELOPMENT
:
7304 case REALM_ZONE_TEST_SERVER
:
7305 case REALM_ZONE_QA_SERVER
:
7307 case REALM_ZONE_UNITED_STATES
: // extended-Latin
7308 case REALM_ZONE_OCEANIC
:
7309 case REALM_ZONE_LATIN_AMERICA
:
7310 case REALM_ZONE_ENGLISH
:
7311 case REALM_ZONE_GERMAN
:
7312 case REALM_ZONE_FRENCH
:
7313 case REALM_ZONE_SPANISH
:
7314 return LT_EXTENDEN_LATIN
;
7315 case REALM_ZONE_KOREA
: // East-Asian
7316 case REALM_ZONE_TAIWAN
:
7317 case REALM_ZONE_CHINA
:
7318 return LT_EAST_ASIA
;
7319 case REALM_ZONE_RUSSIAN
: // Cyrillic
7322 return create
? LT_BASIC_LATIN
: LT_ANY
; // basic-Latin at create, any at login
7326 bool isValidString(std::wstring wstr
, uint32 strictMask
, bool numericOrSpace
, bool create
= false)
7328 if (strictMask
== 0) // any language, ignore realm
7330 if (isExtendedLatinString(wstr
, numericOrSpace
))
7332 if (isCyrillicString(wstr
, numericOrSpace
))
7334 if (isEastAsianString(wstr
, numericOrSpace
))
7339 if (strictMask
& 0x2) // realm zone specific
7341 LanguageType lt
= GetRealmLanguageType(create
);
7342 if (lt
& LT_EXTENDEN_LATIN
)
7343 if (isExtendedLatinString(wstr
, numericOrSpace
))
7345 if (lt
& LT_CYRILLIC
)
7346 if (isCyrillicString(wstr
, numericOrSpace
))
7348 if (lt
& LT_EAST_ASIA
)
7349 if (isEastAsianString(wstr
, numericOrSpace
))
7353 if (strictMask
& 0x1) // basic Latin
7355 if (isBasicLatinString(wstr
, numericOrSpace
))
7362 uint8
ObjectMgr::CheckPlayerName(const std::string
& name
, bool create
)
7365 if (!Utf8toWStr(name
, wname
))
7366 return CHAR_NAME_INVALID_CHARACTER
;
7368 if (wname
.size() > MAX_PLAYER_NAME
)
7369 return CHAR_NAME_TOO_LONG
;
7371 uint32 minName
= sWorld
.getConfig(CONFIG_UINT32_MIN_PLAYER_NAME
);
7372 if (wname
.size() < minName
)
7373 return CHAR_NAME_TOO_SHORT
;
7375 uint32 strictMask
= sWorld
.getConfig(CONFIG_UINT32_STRICT_PLAYER_NAMES
);
7376 if (!isValidString(wname
, strictMask
, false, create
))
7377 return CHAR_NAME_MIXED_LANGUAGES
;
7379 return CHAR_NAME_SUCCESS
;
7382 bool ObjectMgr::IsValidCharterName(const std::string
& name
)
7385 if (!Utf8toWStr(name
, wname
))
7388 if (wname
.size() > MAX_CHARTER_NAME
)
7391 uint32 minName
= sWorld
.getConfig(CONFIG_UINT32_MIN_CHARTER_NAME
);
7392 if (wname
.size() < minName
)
7395 uint32 strictMask
= sWorld
.getConfig(CONFIG_UINT32_STRICT_CHARTER_NAMES
);
7397 return isValidString(wname
, strictMask
, true);
7400 PetNameInvalidReason
ObjectMgr::CheckPetName(const std::string
& name
)
7403 if (!Utf8toWStr(name
, wname
))
7404 return PET_NAME_INVALID
;
7406 if (wname
.size() > MAX_PET_NAME
)
7407 return PET_NAME_TOO_LONG
;
7409 uint32 minName
= sWorld
.getConfig(CONFIG_UINT32_MIN_PET_NAME
);
7410 if (wname
.size() < minName
)
7411 return PET_NAME_TOO_SHORT
;
7413 uint32 strictMask
= sWorld
.getConfig(CONFIG_UINT32_STRICT_PET_NAMES
);
7414 if (!isValidString(wname
, strictMask
, false))
7415 return PET_NAME_MIXED_LANGUAGES
;
7417 return PET_NAME_SUCCESS
;
7420 int ObjectMgr::GetIndexForLocale(LocaleConstant loc
)
7422 if (loc
== LOCALE_enUS
)
7425 for (size_t i
= 0; i
< m_LocalForIndex
.size(); ++i
)
7426 if (m_LocalForIndex
[i
] == loc
)
7432 LocaleConstant
ObjectMgr::GetLocaleForIndex(int i
)
7434 if (i
< 0 || i
>= (int32
)m_LocalForIndex
.size())
7437 return m_LocalForIndex
[i
];
7440 int ObjectMgr::GetOrNewIndexForLocale(LocaleConstant loc
)
7442 if (loc
== LOCALE_enUS
)
7445 for (size_t i
= 0; i
< m_LocalForIndex
.size(); ++i
)
7446 if (m_LocalForIndex
[i
] == loc
)
7449 m_LocalForIndex
.push_back(loc
);
7450 return m_LocalForIndex
.size() - 1;
7453 void ObjectMgr::LoadGameObjectForQuests()
7455 mGameObjectForQuestSet
.clear(); // need for reload case
7457 if (!sGOStorage
.GetMaxEntry())
7462 sLog
.outString(">> Loaded 0 GameObjects for quests");
7466 BarGoLink
bar(sGOStorage
.GetMaxEntry() - 1);
7469 // collect GO entries for GO that must activated
7470 for (uint32 go_entry
= 1; go_entry
< sGOStorage
.GetMaxEntry(); ++go_entry
)
7473 GameObjectInfo
const* goInfo
= GetGameObjectInfo(go_entry
);
7477 switch (goInfo
->type
)
7479 case GAMEOBJECT_TYPE_QUESTGIVER
:
7481 if (m_GOQuestRelations
.find(go_entry
) != m_GOQuestRelations
.end() ||
7482 m_GOQuestInvolvedRelations
.find(go_entry
) != m_GOQuestInvolvedRelations
.end())
7484 mGameObjectForQuestSet
.insert(go_entry
);
7490 case GAMEOBJECT_TYPE_CHEST
:
7492 // scan GO chest with loot including quest items
7493 uint32 loot_id
= goInfo
->GetLootId();
7495 // always activate to quest, GO may not have loot, OR find if GO has loot for quest.
7496 if (goInfo
->chest
.questId
|| LootTemplates_Gameobject
.HaveQuestLootFor(loot_id
))
7498 mGameObjectForQuestSet
.insert(go_entry
);
7503 case GAMEOBJECT_TYPE_GENERIC
:
7505 if (goInfo
->_generic
.questID
) // quest related objects, has visual effects
7507 mGameObjectForQuestSet
.insert(go_entry
);
7512 case GAMEOBJECT_TYPE_SPELL_FOCUS
:
7514 if (goInfo
->spellFocus
.questID
) // quest related objects, has visual effect
7516 mGameObjectForQuestSet
.insert(go_entry
);
7521 case GAMEOBJECT_TYPE_GOOBER
:
7523 if (goInfo
->goober
.questId
) // quests objects
7525 mGameObjectForQuestSet
.insert(go_entry
);
7536 sLog
.outString(">> Loaded %u GameObjects for quests", count
);
7539 bool ObjectMgr::LoadMangosStrings(DatabaseType
& db
, char const* table
, int32 min_value
, int32 max_value
)
7541 int32 start_value
= min_value
;
7542 int32 end_value
= max_value
;
7543 // some string can have negative indexes range
7544 if (start_value
< 0)
7546 if (end_value
>= start_value
)
7548 sLog
.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table
, min_value
, max_value
);
7552 // real range (max+1,min+1) exaple: (-10,-1000) -> -999...-10+1
7553 std::swap(start_value
, end_value
);
7559 if (start_value
>= end_value
)
7561 sLog
.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table
, min_value
, max_value
);
7566 // cleanup affected map part for reloading case
7567 for (MangosStringLocaleMap::iterator itr
= mMangosStringLocaleMap
.begin(); itr
!= mMangosStringLocaleMap
.end();)
7569 if (itr
->first
>= start_value
&& itr
->first
< end_value
)
7570 mMangosStringLocaleMap
.erase(itr
++);
7575 QueryResult
* result
= db
.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s", table
);
7584 if (min_value
== MIN_MANGOS_STRING_ID
) // error only in case internal strings
7585 sLog
.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.", table
);
7587 sLog
.outString(">> Loaded 0 string templates. DB table `%s` is empty.", table
);
7593 BarGoLink
bar(result
->GetRowCount());
7597 Field
* fields
= result
->Fetch();
7600 int32 entry
= fields
[0].GetInt32();
7604 sLog
.outErrorDb("Table `%s` contain reserved entry 0, ignored.", table
);
7607 else if (entry
< start_value
|| entry
>= end_value
)
7609 sLog
.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.", table
, entry
, min_value
, max_value
);
7613 MangosStringLocale
& data
= mMangosStringLocaleMap
[entry
];
7615 if (data
.Content
.size() > 0)
7617 sLog
.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.", table
, entry
);
7621 data
.Content
.resize(1);
7624 // 0 -> default, idx in to idx+1
7625 data
.Content
[0] = fields
[1].GetCppString();
7627 for (int i
= 1; i
< MAX_LOCALE
; ++i
)
7629 std::string str
= fields
[i
+ 1].GetCppString();
7632 int idx
= GetOrNewIndexForLocale(LocaleConstant(i
));
7635 // 0 -> default, idx in to idx+1
7636 if ((int32
)data
.Content
.size() <= idx
+ 1)
7637 data
.Content
.resize(idx
+ 2);
7639 data
.Content
[idx
+ 1] = str
;
7644 while (result
->NextRow());
7649 if (min_value
== MIN_MANGOS_STRING_ID
)
7650 sLog
.outString(">> Loaded %u MaNGOS strings from table %s", count
, table
);
7652 sLog
.outString(">> Loaded %u string templates from %s", count
, table
);
7657 const char* ObjectMgr::GetMangosString(int32 entry
, int locale_idx
) const
7659 // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
7660 // Content[0] always exist if exist MangosStringLocale
7661 if (MangosStringLocale
const* msl
= GetMangosStringLocale(entry
))
7663 if ((int32
)msl
->Content
.size() > locale_idx
+ 1 && !msl
->Content
[locale_idx
+ 1].empty())
7664 return msl
->Content
[locale_idx
+ 1].c_str();
7666 return msl
->Content
[0].c_str();
7669 if (entry
> MIN_DB_SCRIPT_STRING_ID
)
7670 sLog
.outErrorDb("Entry %i not found in `db_script_string` table.", entry
);
7672 sLog
.outErrorDb("Entry %i not found in `mangos_string` table.", entry
);
7673 else if (entry
> MAX_CREATURE_AI_TEXT_STRING_ID
)
7674 sLog
.outErrorDb("Entry %i not found in `creature_ai_texts` table.", entry
);
7676 sLog
.outErrorDb("Mangos string entry %i not found in DB.", entry
);
7680 void ObjectMgr::LoadFishingBaseSkillLevel()
7682 mFishingBaseForArea
.clear(); // for reload case
7685 QueryResult
* result
= WorldDatabase
.Query("SELECT entry,skill FROM skill_fishing_base_level");
7694 sLog
.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
7698 BarGoLink
bar(result
->GetRowCount());
7704 Field
* fields
= result
->Fetch();
7705 uint32 entry
= fields
[0].GetUInt32();
7706 int32 skill
= fields
[1].GetInt32();
7708 AreaTableEntry
const* fArea
= GetAreaEntryByAreaID(entry
);
7711 sLog
.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist", entry
);
7715 mFishingBaseForArea
[entry
] = skill
;
7718 while (result
->NextRow());
7723 sLog
.outString(">> Loaded %u areas for fishing base skill level", count
);
7726 // Check if a player meets condition conditionId
7727 bool ObjectMgr::IsPlayerMeetToCondition(Player
const* pPlayer
, uint16 conditionId
) const
7730 return false; // player not present, return false
7732 if (const PlayerCondition
* condition
= sConditionStorage
.LookupEntry
<PlayerCondition
>(conditionId
))
7733 return condition
->Meets(pPlayer
);
7738 bool ObjectMgr::CheckDeclinedNames(std::wstring mainpart
, DeclinedName
const& names
)
7740 for (int i
= 0; i
< MAX_DECLINED_NAME_CASES
; ++i
)
7743 if (!Utf8toWStr(names
.name
[i
], wname
))
7746 if (mainpart
!= GetMainPartOfName(wname
, i
+ 1))
7752 // Checks if player meets the condition
7753 bool PlayerCondition::Meets(Player
const* player
) const
7756 return false; // player not present, return false
7758 switch (m_condition
)
7762 return !sConditionStorage
.LookupEntry
<PlayerCondition
>(m_value1
)->Meets(player
);
7765 return sConditionStorage
.LookupEntry
<PlayerCondition
>(m_value1
)->Meets(player
) || sConditionStorage
.LookupEntry
<PlayerCondition
>(m_value2
)->Meets(player
);
7768 return sConditionStorage
.LookupEntry
<PlayerCondition
>(m_value1
)->Meets(player
) && sConditionStorage
.LookupEntry
<PlayerCondition
>(m_value2
)->Meets(player
);
7769 case CONDITION_NONE
:
7770 return true; // empty condition, always met
7771 case CONDITION_AURA
:
7772 return player
->HasAura(m_value1
, SpellEffectIndex(m_value2
));
7773 case CONDITION_ITEM
:
7774 return player
->HasItemCount(m_value1
, m_value2
);
7775 case CONDITION_ITEM_EQUIPPED
:
7776 return player
->HasItemOrGemWithIdEquipped(m_value1
, 1);
7777 case CONDITION_AREAID
:
7780 player
->GetZoneAndAreaId(zone
, area
);
7781 return (zone
== m_value1
|| area
== m_value1
) == (m_value2
== 0);
7783 case CONDITION_REPUTATION_RANK_MIN
:
7785 FactionEntry
const* faction
= sFactionStore
.LookupEntry(m_value1
);
7786 return faction
&& player
->GetReputationMgr().GetRank(faction
) >= ReputationRank(m_value2
);
7788 case CONDITION_TEAM
:
7789 return uint32(player
->GetTeam()) == m_value1
;
7790 case CONDITION_SKILL
:
7791 return player
->HasSkill(m_value1
) && player
->GetBaseSkillValue(m_value1
) >= m_value2
;
7792 case CONDITION_QUESTREWARDED
:
7793 return player
->GetQuestRewardStatus(m_value1
);
7794 case CONDITION_QUESTTAKEN
:
7796 return player
->IsCurrentQuest(m_value1
, m_value2
);
7798 case CONDITION_AD_COMMISSION_AURA
:
7800 Unit::SpellAuraHolderMap
const& auras
= player
->GetSpellAuraHolderMap();
7801 for (Unit::SpellAuraHolderMap::const_iterator itr
= auras
.begin(); itr
!= auras
.end(); ++itr
)
7802 if ((itr
->second
->GetSpellProto()->HasAttribute(SPELL_ATTR_CASTABLE_WHILE_MOUNTED
) || itr
->second
->GetSpellProto()->HasAttribute(SPELL_ATTR_UNK4
)) && itr
->second
->GetSpellProto()->SpellVisual
[0] == 3580)
7806 case CONDITION_NO_AURA
:
7807 return !player
->HasAura(m_value1
, SpellEffectIndex(m_value2
));
7808 case CONDITION_ACTIVE_GAME_EVENT
:
7809 return sGameEventMgr
.IsActiveEvent(m_value1
);
7810 case CONDITION_AREA_FLAG
:
7812 if (AreaTableEntry
const* pAreaEntry
= GetAreaEntryByAreaID(player
->GetAreaId()))
7814 if ((!m_value1
|| (pAreaEntry
->flags
& m_value1
)) && (!m_value2
|| !(pAreaEntry
->flags
& m_value2
)))
7819 case CONDITION_RACE_CLASS
:
7820 if ((!m_value1
|| (player
->getRaceMask() & m_value1
)) && (!m_value2
|| (player
->getClassMask() & m_value2
)))
7823 case CONDITION_LEVEL
:
7827 case 0: return player
->getLevel() == m_value1
;
7828 case 1: return player
->getLevel() >= m_value1
;
7829 case 2: return player
->getLevel() <= m_value1
;
7833 case CONDITION_NOITEM
:
7834 return !player
->HasItemCount(m_value1
, m_value2
);
7835 case CONDITION_SPELL
:
7839 case 0: return player
->HasSpell(m_value1
);
7840 case 1: return !player
->HasSpell(m_value1
);
7844 case CONDITION_INSTANCE_SCRIPT
:
7846 // have meaning only for specific map instance script so ignore other maps
7847 if (player
->GetMapId() != m_value1
)
7849 if (InstanceData
* data
= player
->GetInstanceData())
7850 return data
->CheckConditionCriteriaMeet(player
, m_value1
, m_value2
);
7853 case CONDITION_QUESTAVAILABLE
:
7855 if (Quest
const* quest
= sObjectMgr
.GetQuestTemplate(m_value1
))
7856 return player
->CanTakeQuest(quest
, false);
7860 case CONDITION_ACHIEVEMENT
:
7864 case 0: return player
->GetAchievementMgr().HasAchievement(m_value1
);
7865 case 1: return !player
->GetAchievementMgr().HasAchievement(m_value1
);
7869 case CONDITION_ACHIEVEMENT_REALM
:
7871 AchievementEntry
const* ach
= sAchievementStore
.LookupEntry(m_value1
);
7874 case 0: return sAchievementMgr
.IsRealmCompleted(ach
);
7875 case 1: return !sAchievementMgr
.IsRealmCompleted(ach
);
7879 case CONDITION_QUEST_NONE
:
7881 if (!player
->IsCurrentQuest(m_value1
) && !player
->GetQuestRewardStatus(m_value1
))
7885 case CONDITION_ITEM_WITH_BANK
:
7886 return player
->HasItemCount(m_value1
, m_value2
, true);
7887 case CONDITION_NOITEM_WITH_BANK
:
7888 return !player
->HasItemCount(m_value1
, m_value2
, true);
7889 case CONDITION_NOT_ACTIVE_GAME_EVENT
:
7890 return !sGameEventMgr
.IsActiveEvent(m_value1
);
7891 case CONDITION_ACTIVE_HOLIDAY
:
7892 return sGameEventMgr
.IsActiveHoliday(HolidayIds(m_value1
));
7893 case CONDITION_NOT_ACTIVE_HOLIDAY
:
7894 return !sGameEventMgr
.IsActiveHoliday(HolidayIds(m_value1
));
7895 case CONDITION_LEARNABLE_ABILITY
:
7897 // Already know the spell
7898 if (player
->HasSpell(m_value1
))
7901 // If item defined, check if player has the item already.
7904 // Hard coded item count. This should be ok, since the intention with this condition is to have
7905 // a all-in-one check regarding items that learn some ability (primary/secondary tradeskills).
7906 // Commonly, items like this is unique and/or are not expected to be obtained more than once.
7907 if (player
->HasItemCount(m_value2
, 1, true))
7911 bool isSkillOk
= false;
7913 SkillLineAbilityMapBounds bounds
= sSpellMgr
.GetSkillLineAbilityMapBounds(m_value1
);
7915 for (SkillLineAbilityMap::const_iterator itr
= bounds
.first
; itr
!= bounds
.second
; ++itr
)
7917 const SkillLineAbilityEntry
* skillInfo
= itr
->second
;
7922 // doesn't have skill
7923 if (!player
->HasSkill(skillInfo
->skillId
))
7926 // doesn't match class
7927 if (skillInfo
->classmask
&& (skillInfo
->classmask
& player
->getClassMask()) == 0)
7930 // doesn't match race
7931 if (skillInfo
->racemask
&& (skillInfo
->racemask
& player
->getRaceMask()) == 0)
7934 // skill level too low
7935 if (skillInfo
->min_value
> player
->GetSkillValue(skillInfo
->skillId
))
7947 case CONDITION_SKILL_BELOW
:
7949 return !player
->HasSkill(m_value1
);
7951 return player
->HasSkill(m_value1
) && player
->GetBaseSkillValue(m_value1
) < m_value2
;
7952 case CONDITION_REPUTATION_RANK_MAX
:
7954 FactionEntry
const* faction
= sFactionStore
.LookupEntry(m_value1
);
7955 return faction
&& player
->GetReputationMgr().GetRank(faction
) <= ReputationRank(m_value2
);
7957 case CONDITION_COMPLETED_ENCOUNTER
:
7959 if (!player
->GetMap()->IsDungeon())
7961 sLog
.outErrorDb("CONDITION_COMPLETED_ENCOUNTER (entry %u) is used outside of a dungeon (on Map %u) by %s", m_entry
, player
->GetMapId(), player
->GetGuidStr().c_str());
7965 uint32 completedEncounterMask
= ((DungeonMap
*)player
->GetMap())->GetPersistanceState()->GetCompletedEncountersMask();
7966 DungeonEncounterEntry
const* dbcEntry1
= sDungeonEncounterStore
.LookupEntry(m_value1
);
7967 DungeonEncounterEntry
const* dbcEntry2
= sDungeonEncounterStore
.LookupEntry(m_value2
);
7968 // Check that on proper map
7969 if (dbcEntry1
->mapId
!= player
->GetMapId())
7971 sLog
.outErrorDb("CONDITION_COMPLETED_ENCOUNTER (entry %u, DungeonEncounterEntry %u) is used on wrong map (used on Map %u) by %s", m_entry
, m_value1
, player
->GetMapId(), player
->GetGuidStr().c_str());
7974 // Select matching difficulties
7975 if (player
->GetDifficulty(player
->GetMap()->IsRaid()) != Difficulty(dbcEntry1
->Difficulty
))
7977 if (dbcEntry2
&& player
->GetDifficulty(player
->GetMap()->IsRaid()) != Difficulty(dbcEntry2
->Difficulty
))
7980 return completedEncounterMask
& ((dbcEntry1
? 1 << dbcEntry1
->encounterIndex
: 0) | (dbcEntry2
? 1 << dbcEntry2
->encounterIndex
: 0));
7987 // Verification of condition values validity
7988 bool PlayerCondition::IsValid(uint16 entry
, ConditionType condition
, uint32 value1
, uint32 value2
)
7994 if (value1
>= entry
)
7996 sLog
.outErrorDb("CONDITION_NOT (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry
, condition
, value1
);
7999 const PlayerCondition
* condition1
= sConditionStorage
.LookupEntry
<PlayerCondition
>(value1
);
8002 sLog
.outErrorDb("CONDITION_NOT (entry %u, type %d) has value1 %u without proper condition, skipped", entry
, condition
, value1
);
8010 if (value1
>= entry
)
8012 sLog
.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry
, condition
, value1
);
8015 if (value2
>= entry
)
8017 sLog
.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value2 %u, must be lower than entry, skipped", entry
, condition
, value2
);
8020 const PlayerCondition
* condition1
= sConditionStorage
.LookupEntry
<PlayerCondition
>(value1
);
8023 sLog
.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value1 %u without proper condition, skipped", entry
, condition
, value1
);
8026 const PlayerCondition
* condition2
= sConditionStorage
.LookupEntry
<PlayerCondition
>(value2
);
8029 sLog
.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value2 %u without proper condition, skipped", entry
, condition
, value2
);
8034 case CONDITION_AURA
:
8036 if (!sSpellStore
.LookupEntry(value1
))
8038 sLog
.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry
, condition
, value1
);
8041 if (value2
>= MAX_EFFECT_INDEX
)
8043 sLog
.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", entry
, condition
, value2
, MAX_EFFECT_INDEX
- 1);
8048 case CONDITION_ITEM
:
8049 case CONDITION_NOITEM
:
8050 case CONDITION_ITEM_WITH_BANK
:
8051 case CONDITION_NOITEM_WITH_BANK
:
8053 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(value1
);
8056 sLog
.outErrorDb("Item condition (entry %u, type %u) requires to have non existing item (%u), skipped", entry
, condition
, value1
);
8062 sLog
.outErrorDb("Item condition (entry %u, type %u) useless with count < 1, skipped", entry
, condition
);
8067 case CONDITION_ITEM_EQUIPPED
:
8069 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(value1
);
8072 sLog
.outErrorDb("ItemEquipped condition (entry %u, type %u) requires to have non existing item (%u) equipped, skipped", entry
, condition
, value1
);
8077 case CONDITION_AREAID
:
8079 AreaTableEntry
const* areaEntry
= GetAreaEntryByAreaID(value1
);
8082 sLog
.outErrorDb("Zone condition (entry %u, type %u) requires to be in non existing area (%u), skipped", entry
, condition
, value1
);
8088 sLog
.outErrorDb("Zone condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry
, condition
, value2
);
8093 case CONDITION_REPUTATION_RANK_MIN
:
8094 case CONDITION_REPUTATION_RANK_MAX
:
8096 FactionEntry
const* factionEntry
= sFactionStore
.LookupEntry(value1
);
8099 sLog
.outErrorDb("Reputation condition (entry %u, type %u) requires to have reputation non existing faction (%u), skipped", entry
, condition
, value1
);
8103 if (value2
>= MAX_REPUTATION_RANK
)
8105 sLog
.outErrorDb("Reputation condition (entry %u, type %u) has invalid rank requirement (value2 = %u) - must be between %u and %u, skipped", entry
, condition
, value2
, MIN_REPUTATION_RANK
, MAX_REPUTATION_RANK
- 1);
8110 case CONDITION_TEAM
:
8112 if (value1
!= ALLIANCE
&& value1
!= HORDE
)
8114 sLog
.outErrorDb("Team condition (entry %u, type %u) specifies unknown team (%u), skipped", entry
, condition
, value1
);
8119 case CONDITION_SKILL
:
8120 case CONDITION_SKILL_BELOW
:
8122 SkillLineEntry
const* pSkill
= sSkillLineStore
.LookupEntry(value1
);
8125 sLog
.outErrorDb("Skill condition (entry %u, type %u) specifies non-existing skill (%u), skipped", entry
, condition
, value1
);
8128 if (value2
< 1 || value2
> sWorld
.GetConfigMaxSkillValue())
8130 sLog
.outErrorDb("Skill condition (entry %u, type %u) specifies invalid skill value (%u), skipped", entry
, condition
, value2
);
8135 case CONDITION_QUESTREWARDED
:
8136 case CONDITION_QUESTTAKEN
:
8137 case CONDITION_QUESTAVAILABLE
:
8138 case CONDITION_QUEST_NONE
:
8140 Quest
const* Quest
= sObjectMgr
.GetQuestTemplate(value1
);
8143 sLog
.outErrorDb("Quest condition (entry %u, type %u) specifies non-existing quest (%u), skipped", entry
, condition
, value1
);
8147 if (value2
&& condition
!= CONDITION_QUESTTAKEN
)
8148 sLog
.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry
, condition
, value2
);
8151 case CONDITION_AD_COMMISSION_AURA
:
8154 sLog
.outErrorDb("Quest condition (entry %u, type %u) has useless data in value1 (%u)!", entry
, condition
, value1
);
8156 sLog
.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry
, condition
, value2
);
8159 case CONDITION_NO_AURA
:
8161 if (!sSpellStore
.LookupEntry(value1
))
8163 sLog
.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry
, condition
, value1
);
8166 if (value2
> MAX_EFFECT_INDEX
)
8168 sLog
.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", entry
, condition
, value2
, MAX_EFFECT_INDEX
- 1);
8173 case CONDITION_ACTIVE_GAME_EVENT
:
8174 case CONDITION_NOT_ACTIVE_GAME_EVENT
:
8176 if (!sGameEventMgr
.IsValidEvent(value1
))
8178 sLog
.outErrorDb("(Not)Active event condition (entry %u, type %u) requires existing event id (%u), skipped", entry
, condition
, value1
);
8183 case CONDITION_AREA_FLAG
:
8185 if (!value1
&& !value2
)
8187 sLog
.outErrorDb("Area flag condition (entry %u, type %u) has both values like 0, skipped", entry
, condition
);
8192 case CONDITION_RACE_CLASS
:
8194 if (!value1
&& !value2
)
8196 sLog
.outErrorDb("Race_class condition (entry %u, type %u) has both values like 0, skipped", entry
, condition
);
8200 if (value1
&& !(value1
& RACEMASK_ALL_PLAYABLE
))
8202 sLog
.outErrorDb("Race_class condition (entry %u, type %u) has invalid player class %u, skipped", entry
, condition
, value1
);
8206 if (value2
&& !(value2
& CLASSMASK_ALL_PLAYABLE
))
8208 sLog
.outErrorDb("Race_class condition (entry %u, type %u) has invalid race mask %u, skipped", entry
, condition
, value2
);
8213 case CONDITION_LEVEL
:
8215 if (!value1
|| value1
> sWorld
.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL
))
8217 sLog
.outErrorDb("Level condition (entry %u, type %u)has invalid level %u, skipped", entry
, condition
, value1
);
8223 sLog
.outErrorDb("Level condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", entry
, condition
, value2
);
8229 case CONDITION_SPELL
:
8231 if (!sSpellStore
.LookupEntry(value1
))
8233 sLog
.outErrorDb("Spell condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry
, condition
, value1
);
8239 sLog
.outErrorDb("Spell condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry
, condition
, value2
);
8245 case CONDITION_INSTANCE_SCRIPT
:
8247 MapEntry
const* mapEntry
= sMapStore
.LookupEntry(value1
);
8248 if (!mapEntry
|| !mapEntry
->IsDungeon())
8250 sLog
.outErrorDb("Instance script condition (entry %u, type %u) has nonexistent map id %u as first arg, skipped", entry
, condition
, value1
);
8256 case CONDITION_ACHIEVEMENT
:
8257 case CONDITION_ACHIEVEMENT_REALM
:
8259 if (!sAchievementStore
.LookupEntry(value1
))
8261 sLog
.outErrorDb("Achievement condition (entry %u, type %u) requires to have non existing achievement (Id: %d), skipped", entry
, condition
, value1
);
8267 sLog
.outErrorDb("Achievement condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry
, condition
, value2
);
8273 case CONDITION_ACTIVE_HOLIDAY
:
8274 case CONDITION_NOT_ACTIVE_HOLIDAY
:
8276 if (!sHolidaysStore
.LookupEntry(value1
))
8278 sLog
.outErrorDb("(Not)Active holiday condition (entry %u, type %u) requires existing holiday id (%u), skipped", entry
, condition
, value1
);
8283 case CONDITION_LEARNABLE_ABILITY
:
8285 SkillLineAbilityMapBounds bounds
= sSpellMgr
.GetSkillLineAbilityMapBounds(value1
);
8287 if (bounds
.first
== bounds
.second
)
8289 sLog
.outErrorDb("Learnable ability condition (entry %u, type %u) has spell id %u defined, but this spell is not listed in SkillLineAbility and can not be used, skipping.", entry
, condition
, value1
);
8295 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(value2
);
8298 sLog
.outErrorDb("Learnable ability condition (entry %u, type %u) has item entry %u defined but item does not exist, skipping.", entry
, condition
, value2
);
8305 case CONDITION_COMPLETED_ENCOUNTER
:
8307 DungeonEncounterEntry
const* dbcEntry1
= sDungeonEncounterStore
.LookupEntry(value1
);
8308 DungeonEncounterEntry
const* dbcEntry2
= sDungeonEncounterStore
.LookupEntry(value2
);
8311 sLog
.outErrorDb("Completed Encounter condition (entry %u, type %u) has an unknown DungeonEncounter entry %u defined (in value1), skipping.", entry
, condition
, value1
);
8314 if (value2
&& !dbcEntry2
)
8316 sLog
.outErrorDb("Completed Encounter condition (entry %u, type %u) has an unknown DungeonEncounter entry %u defined (in value2), skipping.", entry
, condition
, value2
);
8319 if (dbcEntry2
&& dbcEntry1
->mapId
!= dbcEntry2
->mapId
)
8321 sLog
.outErrorDb("Completed Encounter condition (entry %u, type %u) has different mapIds for both encounters, skipping.", entry
, condition
);
8326 case CONDITION_NONE
:
8329 sLog
.outErrorDb("Condition entry %u has bad type of %d, skipped ", entry
, condition
);
8335 SkillRangeType
GetSkillRangeType(SkillLineEntry
const* pSkill
, bool racial
)
8337 switch (pSkill
->categoryId
)
8339 case SKILL_CATEGORY_LANGUAGES
:
8340 return SKILL_RANGE_LANGUAGE
;
8341 case SKILL_CATEGORY_WEAPON
:
8342 return SKILL_RANGE_LEVEL
;
8343 case SKILL_CATEGORY_ARMOR
:
8344 case SKILL_CATEGORY_CLASS
:
8345 return SKILL_RANGE_MONO
;
8346 case SKILL_CATEGORY_SECONDARY
:
8347 case SKILL_CATEGORY_PROFESSION
:
8348 // not set skills for professions and racial abilities
8349 if (IsProfessionSkill(pSkill
->id
))
8350 return SKILL_RANGE_RANK
;
8352 return SKILL_RANGE_NONE
;
8354 return SKILL_RANGE_MONO
;
8356 case SKILL_CATEGORY_ATTRIBUTES
: // not found in dbc
8357 case SKILL_CATEGORY_GENERIC
: // only GENERIC(DND)
8358 return SKILL_RANGE_NONE
;
8362 void ObjectMgr::LoadGameTele()
8364 m_GameTeleMap
.clear(); // for reload case
8367 QueryResult
* result
= WorldDatabase
.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
8376 sLog
.outErrorDb(">> Loaded `game_tele`, table is empty!");
8380 BarGoLink
bar(result
->GetRowCount());
8386 Field
* fields
= result
->Fetch();
8388 uint32 id
= fields
[0].GetUInt32();
8392 gt
.position_x
= fields
[1].GetFloat();
8393 gt
.position_y
= fields
[2].GetFloat();
8394 gt
.position_z
= fields
[3].GetFloat();
8395 gt
.orientation
= fields
[4].GetFloat();
8396 gt
.mapId
= fields
[5].GetUInt32();
8397 gt
.name
= fields
[6].GetCppString();
8399 if (!MapManager::IsValidMapCoord(gt
.mapId
, gt
.position_x
, gt
.position_y
, gt
.position_z
, gt
.orientation
))
8401 sLog
.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id
, gt
.name
.c_str());
8405 if (!Utf8toWStr(gt
.name
, gt
.wnameLow
))
8407 sLog
.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id
);
8411 wstrToLower(gt
.wnameLow
);
8413 m_GameTeleMap
[id
] = gt
;
8417 while (result
->NextRow());
8421 sLog
.outString(">> Loaded %u GameTeleports", count
);
8424 GameTele
const* ObjectMgr::GetGameTele(const std::string
& name
) const
8426 // explicit name case
8428 if (!Utf8toWStr(name
, wname
))
8431 // converting string that we try to find to lower case
8434 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
8435 const GameTele
* alt
= NULL
;
8436 for (GameTeleMap::const_iterator itr
= m_GameTeleMap
.begin(); itr
!= m_GameTeleMap
.end(); ++itr
)
8437 if (itr
->second
.wnameLow
== wname
)
8438 return &itr
->second
;
8439 else if (alt
== NULL
&& itr
->second
.wnameLow
.find(wname
) != std::wstring::npos
)
8445 bool ObjectMgr::AddGameTele(GameTele
& tele
)
8449 for (GameTeleMap::const_iterator itr
= m_GameTeleMap
.begin(); itr
!= m_GameTeleMap
.end(); ++itr
)
8450 if (itr
->first
> new_id
)
8451 new_id
= itr
->first
;
8456 if (!Utf8toWStr(tele
.name
, tele
.wnameLow
))
8459 wstrToLower(tele
.wnameLow
);
8461 m_GameTeleMap
[new_id
] = tele
;
8462 std::string
safeName(tele
.name
);
8463 WorldDatabase
.escape_string(safeName
);
8465 return WorldDatabase
.PExecuteLog("INSERT INTO game_tele "
8466 "(id,position_x,position_y,position_z,orientation,map,name) "
8467 "VALUES (%u,%f,%f,%f,%f,%u,'%s')",
8468 new_id
, tele
.position_x
, tele
.position_y
, tele
.position_z
,
8469 tele
.orientation
, tele
.mapId
, safeName
.c_str());
8472 bool ObjectMgr::DeleteGameTele(const std::string
& name
)
8474 // explicit name case
8476 if (!Utf8toWStr(name
, wname
))
8479 // converting string that we try to find to lower case
8482 for (GameTeleMap::iterator itr
= m_GameTeleMap
.begin(); itr
!= m_GameTeleMap
.end(); ++itr
)
8484 if (itr
->second
.wnameLow
== wname
)
8486 WorldDatabase
.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'", itr
->second
.name
.c_str());
8487 m_GameTeleMap
.erase(itr
);
8495 void ObjectMgr::LoadMailLevelRewards()
8497 m_mailLevelRewardMap
.clear(); // for reload case
8500 QueryResult
* result
= WorldDatabase
.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");
8509 sLog
.outErrorDb(">> Loaded `mail_level_reward`, table is empty!");
8513 BarGoLink
bar(result
->GetRowCount());
8519 Field
* fields
= result
->Fetch();
8521 uint8 level
= fields
[0].GetUInt8();
8522 uint32 raceMask
= fields
[1].GetUInt32();
8523 uint32 mailTemplateId
= fields
[2].GetUInt32();
8524 uint32 senderEntry
= fields
[3].GetUInt32();
8526 if (level
> MAX_LEVEL
)
8528 sLog
.outErrorDb("Table `mail_level_reward` have data for level %u that more supported by client (%u), ignoring.", level
, MAX_LEVEL
);
8532 if (!(raceMask
& RACEMASK_ALL_PLAYABLE
))
8534 sLog
.outErrorDb("Table `mail_level_reward` have raceMask (%u) for level %u that not include any player races, ignoring.", raceMask
, level
);
8538 if (!sMailTemplateStore
.LookupEntry(mailTemplateId
))
8540 sLog
.outErrorDb("Table `mail_level_reward` have invalid mailTemplateId (%u) for level %u that invalid not include any player races, ignoring.", mailTemplateId
, level
);
8544 if (!GetCreatureTemplate(senderEntry
))
8546 sLog
.outErrorDb("Table `mail_level_reward` have nonexistent sender creature entry (%u) for level %u that invalid not include any player races, ignoring.", senderEntry
, level
);
8550 m_mailLevelRewardMap
[level
].push_back(MailLevelReward(raceMask
, mailTemplateId
, senderEntry
));
8554 while (result
->NextRow());
8558 sLog
.outString(">> Loaded %u level dependent mail rewards,", count
);
8561 void ObjectMgr::LoadTrainers(char const* tableName
, bool isTemplates
)
8563 CacheTrainerSpellMap
& trainerList
= isTemplates
? m_mCacheTrainerTemplateSpellMap
: m_mCacheTrainerSpellMap
;
8566 for (CacheTrainerSpellMap::iterator itr
= trainerList
.begin(); itr
!= trainerList
.end(); ++itr
)
8567 itr
->second
.Clear();
8568 trainerList
.clear();
8570 std::set
<uint32
> skip_trainers
;
8572 QueryResult
* result
= WorldDatabase
.PQuery("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM %s", tableName
);
8581 sLog
.outString(">> Loaded `%s`, table is empty!", tableName
);
8585 BarGoLink
bar(result
->GetRowCount());
8587 std::set
<uint32
> talentIds
;
8594 Field
* fields
= result
->Fetch();
8596 uint32 entry
= fields
[0].GetUInt32();
8597 uint32 spell
= fields
[1].GetUInt32();
8599 SpellEntry
const* spellinfo
= sSpellStore
.LookupEntry(spell
);
8602 sLog
.outErrorDb("Table `%s` (Entry: %u ) has non existing spell %u, ignore", tableName
, entry
, spell
);
8606 if (!SpellMgr::IsSpellValid(spellinfo
))
8608 sLog
.outErrorDb("Table `%s` (Entry: %u) has broken learning spell %u, ignore", tableName
, entry
, spell
);
8612 if (GetTalentSpellCost(spell
))
8614 if (talentIds
.find(spell
) == talentIds
.end())
8616 sLog
.outErrorDb("Table `%s` has talent as learning spell %u, ignore", tableName
, spell
);
8617 talentIds
.insert(spell
);
8624 CreatureInfo
const* cInfo
= GetCreatureTemplate(entry
);
8628 sLog
.outErrorDb("Table `%s` have entry for nonexistent creature template (Entry: %u), ignore", tableName
, entry
);
8632 if (!(cInfo
->npcflag
& UNIT_NPC_FLAG_TRAINER
))
8634 if (skip_trainers
.find(entry
) == skip_trainers
.end())
8636 sLog
.outErrorDb("Table `%s` have data for creature (Entry: %u) without trainer flag, ignore", tableName
, entry
);
8637 skip_trainers
.insert(entry
);
8642 if (TrainerSpellData
const* tSpells
= cInfo
->trainerId
? GetNpcTrainerTemplateSpells(cInfo
->trainerId
) : NULL
)
8644 if (tSpells
->spellList
.find(spell
) != tSpells
->spellList
.end())
8646 sLog
.outErrorDb("Table `%s` (Entry: %u) has spell %u listed in trainer template %u, ignore", tableName
, entry
, spell
, cInfo
->trainerId
);
8652 TrainerSpellData
& data
= trainerList
[entry
];
8654 TrainerSpell
& trainerSpell
= data
.spellList
[spell
];
8655 trainerSpell
.spell
= spell
;
8656 trainerSpell
.spellCost
= fields
[2].GetUInt32();
8657 trainerSpell
.reqSkill
= fields
[3].GetUInt32();
8658 trainerSpell
.reqSkillValue
= fields
[4].GetUInt32();
8659 trainerSpell
.reqLevel
= fields
[5].GetUInt32();
8661 trainerSpell
.isProvidedReqLevel
= trainerSpell
.reqLevel
> 0;
8663 // calculate learned spell for profession case when stored cast-spell
8664 trainerSpell
.learnedSpell
= spell
;
8665 for (int i
= 0; i
< MAX_EFFECT_INDEX
; ++i
)
8667 SpellEffectEntry
const* spellEffect
= spellinfo
->GetSpellEffect(SpellEffectIndex(i
));
8671 if (spellEffect
->Effect
== SPELL_EFFECT_LEARN_SPELL
&&
8672 SpellMgr::IsProfessionOrRidingSpell(spellEffect
->EffectTriggerSpell
))
8674 // prof spells sometime only additions to main spell learn that have level data
8675 for (int j
= 0; j
< MAX_EFFECT_INDEX
; ++j
)
8677 SpellEffectEntry
const* spellEff
= spellinfo
->GetSpellEffect(SpellEffectIndex(j
));
8681 if (spellEff
->Effect
== SPELL_EFFECT_LEARN_SPELL
)
8683 trainerSpell
.learnedSpell
= spellEff
->EffectTriggerSpell
;
8691 // already checked as valid spell so exist.
8692 SpellEntry
const* learnSpellinfo
= sSpellStore
.LookupEntry(trainerSpell
.learnedSpell
);
8693 if (SpellMgr::IsProfessionSpell(trainerSpell
.learnedSpell
))
8695 data
.trainerType
= 2;
8697 uint32 minLevel
= sSpellMgr
.GetProfessionSpellMinLevel(trainerSpell
.learnedSpell
);
8698 if (trainerSpell
.reqLevel
)
8700 if (minLevel
== trainerSpell
.reqLevel
)
8701 ERROR_DB_STRICT_LOG("Table `%s` (Entry: %u) has redundant reqlevel %u (=prof reqlevel) for spell %u", tableName
, entry
, trainerSpell
.reqLevel
, spell
);
8703 sLog
.outErrorDb("Table `%s` (Entry: %u) has wrong redundant reqlevel %u (<>prof reqlevel %u) for spell %u", tableName
, entry
, trainerSpell
.reqLevel
, minLevel
, spell
);
8706 trainerSpell
.reqLevel
= minLevel
;
8708 // for non-prof. spell use spellLevel if not provided any
8711 if (trainerSpell
.reqLevel
)
8713 if (trainerSpell
.reqLevel
== learnSpellinfo
->GetSpellLevel())
8714 ERROR_DB_STRICT_LOG("Table `%s` (Entry: %u) has redundant reqlevel %u (=spell level) for spell %u", tableName
, entry
, trainerSpell
.reqLevel
, spell
);
8717 trainerSpell
.reqLevel
= learnSpellinfo
->GetSpellLevel();
8723 while (result
->NextRow());
8727 sLog
.outString(">> Loaded %d trainer %sspells", count
, isTemplates
? "template " : "");
8730 void ObjectMgr::LoadTrainerTemplates()
8732 LoadTrainers("npc_trainer_template", true);
8734 // post loading check
8735 std::set
<uint32
> trainer_ids
;
8737 for (CacheTrainerSpellMap::const_iterator tItr
= m_mCacheTrainerTemplateSpellMap
.begin(); tItr
!= m_mCacheTrainerTemplateSpellMap
.end(); ++tItr
)
8738 trainer_ids
.insert(tItr
->first
);
8740 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
8742 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
8744 if (cInfo
->trainerId
)
8746 if (m_mCacheTrainerTemplateSpellMap
.find(cInfo
->trainerId
) != m_mCacheTrainerTemplateSpellMap
.end())
8747 trainer_ids
.erase(cInfo
->trainerId
);
8749 sLog
.outErrorDb("Creature (Entry: %u) has trainer_id = %u for nonexistent trainer template", cInfo
->Entry
, cInfo
->trainerId
);
8754 for (std::set
<uint32
>::const_iterator tItr
= trainer_ids
.begin(); tItr
!= trainer_ids
.end(); ++tItr
)
8755 sLog
.outErrorDb("Table `npc_trainer_template` has trainer template %u not used by any trainers ", *tItr
);
8758 void ObjectMgr::LoadVendors(char const* tableName
, bool isTemplates
)
8760 CacheVendorItemMap
& vendorList
= isTemplates
? m_mCacheVendorTemplateItemMap
: m_mCacheVendorItemMap
;
8763 for (CacheVendorItemMap::iterator itr
= vendorList
.begin(); itr
!= vendorList
.end(); ++itr
)
8764 itr
->second
.Clear();
8767 std::set
<uint32
> skip_vendors
;
8769 QueryResult
* result
= WorldDatabase
.PQuery("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM %s", tableName
);
8777 sLog
.outString(">> Loaded `%s`, table is empty!", tableName
);
8781 BarGoLink
bar(result
->GetRowCount());
8787 Field
* fields
= result
->Fetch();
8789 uint32 entry
= fields
[0].GetUInt32();
8790 uint32 item_id
= abs(fields
[1].GetInt32());
8791 uint8 type
= fields
[1].GetInt32() > 0 ? VENDOR_ITEM_TYPE_ITEM
: VENDOR_ITEM_TYPE_CURRENCY
;
8792 uint32 maxcount
= fields
[2].GetUInt32();
8793 uint32 incrtime
= fields
[3].GetUInt32();
8794 uint32 ExtendedCost
= fields
[4].GetUInt32();
8796 if (!IsVendorItemValid(isTemplates
, tableName
, entry
, item_id
, type
, maxcount
, incrtime
, ExtendedCost
, NULL
, &skip_vendors
))
8799 VendorItemData
& vList
= vendorList
[entry
];
8801 vList
.AddItem(item_id
, type
, maxcount
, incrtime
, ExtendedCost
);
8805 while (result
->NextRow());
8809 sLog
.outString(">> Loaded %u vendor %sitems", count
, isTemplates
? "template " : "");
8813 void ObjectMgr::LoadVendorTemplates()
8815 LoadVendors("npc_vendor_template", true);
8817 // post loading check
8818 std::set
<uint32
> vendor_ids
;
8820 for (CacheVendorItemMap::const_iterator vItr
= m_mCacheVendorTemplateItemMap
.begin(); vItr
!= m_mCacheVendorTemplateItemMap
.end(); ++vItr
)
8821 vendor_ids
.insert(vItr
->first
);
8823 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
8825 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
8827 if (cInfo
->vendorId
)
8829 if (m_mCacheVendorTemplateItemMap
.find(cInfo
->vendorId
) != m_mCacheVendorTemplateItemMap
.end())
8830 vendor_ids
.erase(cInfo
->vendorId
);
8832 sLog
.outErrorDb("Creature (Entry: %u) has vendor_id = %u for nonexistent vendor template", cInfo
->Entry
, cInfo
->vendorId
);
8837 for (std::set
<uint32
>::const_iterator vItr
= vendor_ids
.begin(); vItr
!= vendor_ids
.end(); ++vItr
)
8838 sLog
.outErrorDb("Table `npc_vendor_template` has vendor template %u not used by any vendors ", *vItr
);
8841 void ObjectMgr::LoadNpcGossips()
8844 m_mCacheNpcTextIdMap
.clear();
8846 QueryResult
* result
= WorldDatabase
.Query("SELECT npc_guid, textid FROM npc_gossip");
8854 sLog
.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
8858 BarGoLink
bar(result
->GetRowCount());
8861 uint32 guid
, textid
;
8866 Field
* fields
= result
->Fetch();
8868 guid
= fields
[0].GetUInt32();
8869 textid
= fields
[1].GetUInt32();
8871 if (!GetCreatureData(guid
))
8873 sLog
.outErrorDb("Table `npc_gossip` have nonexistent creature (GUID: %u) entry, ignore. ", guid
);
8876 if (!GetGossipText(textid
))
8878 sLog
.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid
, textid
);
8882 m_mCacheNpcTextIdMap
[guid
] = textid
;
8886 while (result
->NextRow());
8890 sLog
.outString(">> Loaded %d NpcTextId ", count
);
8893 void ObjectMgr::LoadGossipMenu(std::set
<uint32
>& gossipScriptSet
)
8895 m_mGossipMenusMap
.clear();
8897 QueryResult
* result
= WorldDatabase
.Query("SELECT entry, text_id, script_id, "
8899 "condition_id FROM gossip_menu");
8908 sLog
.outErrorDb(">> Loaded gossip_menu, table is empty!");
8912 BarGoLink
bar(result
->GetRowCount());
8920 Field
* fields
= result
->Fetch();
8924 gMenu
.entry
= fields
[0].GetUInt32();
8925 gMenu
.text_id
= fields
[1].GetUInt32();
8926 gMenu
.script_id
= fields
[2].GetUInt32();
8928 gMenu
.conditionId
= fields
[3].GetUInt16();
8930 if (!GetGossipText(gMenu
.text_id
))
8932 sLog
.outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu
.entry
, gMenu
.text_id
);
8937 if (gMenu
.script_id
)
8939 if (sGossipScripts
.second
.find(gMenu
.script_id
) == sGossipScripts
.second
.end())
8941 sLog
.outErrorDb("Table gossip_menu for menu %u, text-id %u have script_id %u that does not exist in `gossip_scripts`, ignoring", gMenu
.entry
, gMenu
.text_id
, gMenu
.script_id
);
8945 // Remove used script id
8946 gossipScriptSet
.erase(gMenu
.script_id
);
8949 if (gMenu
.conditionId
)
8951 const PlayerCondition
* condition
= sConditionStorage
.LookupEntry
<PlayerCondition
>(gMenu
.conditionId
);
8954 sLog
.outErrorDb("Table gossip_menu for menu %u, text-id %u has condition_id %u that does not exist in `conditions`, ignoring", gMenu
.entry
, gMenu
.text_id
, gMenu
.conditionId
);
8955 gMenu
.conditionId
= 0;
8959 m_mGossipMenusMap
.insert(GossipMenusMap::value_type(gMenu
.entry
, gMenu
));
8963 while (result
->NextRow());
8968 sLog
.outString(">> Loaded %u gossip_menu entries", count
);
8970 // post loading tests
8971 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
8972 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
8973 if (cInfo
->GossipMenuId
)
8974 if (m_mGossipMenusMap
.find(cInfo
->GossipMenuId
) == m_mGossipMenusMap
.end())
8975 sLog
.outErrorDb("Creature (Entry: %u) has gossip_menu_id = %u for nonexistent menu", cInfo
->Entry
, cInfo
->GossipMenuId
);
8977 for (uint32 i
= 1; i
< sGOStorage
.GetMaxEntry(); ++i
)
8978 if (GameObjectInfo
const* gInfo
= sGOStorage
.LookupEntry
<GameObjectInfo
>(i
))
8979 if (uint32 menuid
= gInfo
->GetGossipMenuId())
8980 if (m_mGossipMenusMap
.find(menuid
) == m_mGossipMenusMap
.end())
8981 ERROR_DB_STRICT_LOG("Gameobject (Entry: %u) has gossip_menu_id = %u for nonexistent menu", gInfo
->id
, menuid
);
8984 void ObjectMgr::LoadGossipMenuItems(std::set
<uint32
>& gossipScriptSet
)
8986 m_mGossipMenuItemsMap
.clear();
8988 QueryResult
* result
= WorldDatabase
.Query(
8989 "SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, "
8990 "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text, "
8992 "FROM gossip_menu_option");
9001 sLog
.outErrorDb(">> Loaded gossip_menu_option, table is empty!");
9005 // prepare data for unused menu ids
9006 std::set
<uint32
> menu_ids
; // for later integrity check
9007 if (!sLog
.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK
)) // check unused menu ids only in strict mode
9009 for (GossipMenusMap::const_iterator itr
= m_mGossipMenusMap
.begin(); itr
!= m_mGossipMenusMap
.end(); ++itr
)
9011 menu_ids
.insert(itr
->first
);
9013 for (uint32 i
= 1; i
< sGOStorage
.GetMaxEntry(); ++i
)
9014 if (GameObjectInfo
const* gInfo
= sGOStorage
.LookupEntry
<GameObjectInfo
>(i
))
9015 if (uint32 menuid
= gInfo
->GetGossipMenuId())
9016 menu_ids
.erase(menuid
);
9020 BarGoLink
bar(result
->GetRowCount());
9024 // prepare menuid -> CreatureInfo map for fast access
9025 typedef std::multimap
<uint32
, const CreatureInfo
*> Menu2CInfoMap
;
9026 Menu2CInfoMap menu2CInfoMap
;
9027 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
9028 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
9029 if (cInfo
->GossipMenuId
)
9031 menu2CInfoMap
.insert(Menu2CInfoMap::value_type(cInfo
->GossipMenuId
, cInfo
));
9033 // unused check data preparing part
9034 if (!sLog
.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK
))
9035 menu_ids
.erase(cInfo
->GossipMenuId
);
9042 Field
* fields
= result
->Fetch();
9044 GossipMenuItems gMenuItem
;
9046 gMenuItem
.menu_id
= fields
[0].GetUInt32();
9047 gMenuItem
.id
= fields
[1].GetUInt32();
9048 gMenuItem
.option_icon
= fields
[2].GetUInt8();
9049 gMenuItem
.option_text
= fields
[3].GetCppString();
9050 gMenuItem
.option_id
= fields
[4].GetUInt32();
9051 gMenuItem
.npc_option_npcflag
= fields
[5].GetUInt32();
9052 gMenuItem
.action_menu_id
= fields
[6].GetInt32();
9053 gMenuItem
.action_poi_id
= fields
[7].GetUInt32();
9054 gMenuItem
.action_script_id
= fields
[8].GetUInt32();
9055 gMenuItem
.box_coded
= fields
[9].GetUInt8() != 0;
9056 gMenuItem
.box_money
= fields
[10].GetUInt32();
9057 gMenuItem
.box_text
= fields
[11].GetCppString();
9059 gMenuItem
.conditionId
= fields
[12].GetUInt16();
9061 if (gMenuItem
.menu_id
) // == 0 id is special and not have menu_id data
9063 if (m_mGossipMenusMap
.find(gMenuItem
.menu_id
) == m_mGossipMenusMap
.end())
9065 sLog
.outErrorDb("Gossip menu option (MenuId: %u) for nonexistent menu", gMenuItem
.menu_id
);
9070 if (gMenuItem
.action_menu_id
> 0)
9072 if (m_mGossipMenusMap
.find(gMenuItem
.action_menu_id
) == m_mGossipMenusMap
.end())
9073 sLog
.outErrorDb("Gossip menu option (MenuId: %u Id: %u) have action_menu_id = %u for nonexistent menu", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.action_menu_id
);
9074 else if (!sLog
.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK
))
9075 menu_ids
.erase(gMenuItem
.action_menu_id
);
9078 if (gMenuItem
.option_icon
>= GOSSIP_ICON_MAX
)
9080 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.option_icon
);
9081 gMenuItem
.option_icon
= GOSSIP_ICON_CHAT
;
9084 if (gMenuItem
.option_id
== GOSSIP_OPTION_NONE
)
9085 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u use option id GOSSIP_OPTION_NONE. Option will never be used", gMenuItem
.menu_id
, gMenuItem
.id
);
9087 if (gMenuItem
.option_id
>= GOSSIP_OPTION_MAX
)
9088 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown option id %u. Option will not be used", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.option_id
);
9090 if (gMenuItem
.menu_id
&& gMenuItem
.npc_option_npcflag
)
9092 bool found_menu_uses
= false;
9093 bool found_flags_uses
= false;
9095 std::pair
<Menu2CInfoMap::const_iterator
, Menu2CInfoMap::const_iterator
> tm_bounds
= menu2CInfoMap
.equal_range(gMenuItem
.menu_id
);
9096 for (Menu2CInfoMap::const_iterator it2
= tm_bounds
.first
; !found_flags_uses
&& it2
!= tm_bounds
.second
; ++it2
)
9098 CreatureInfo
const* cInfo
= it2
->second
;
9100 found_menu_uses
= true;
9102 // some from creatures with gossip menu can use gossip option base at npc_flags
9103 if (gMenuItem
.npc_option_npcflag
& cInfo
->npcflag
)
9104 found_flags_uses
= true;
9107 if (found_menu_uses
&& !found_flags_uses
)
9108 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u has `npc_option_npcflag` = %u but creatures using this menu does not have corresponding`npcflag`. Option will not accessible in game.", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.npc_option_npcflag
);
9111 if (gMenuItem
.action_poi_id
&& !GetPointOfInterest(gMenuItem
.action_poi_id
))
9113 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.action_poi_id
);
9114 gMenuItem
.action_poi_id
= 0;
9117 if (gMenuItem
.action_script_id
)
9119 if (sGossipScripts
.second
.find(gMenuItem
.action_script_id
) == sGossipScripts
.second
.end())
9121 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u that does not exist in `gossip_scripts`, ignoring", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.action_script_id
);
9125 // Remove used script id
9126 gossipScriptSet
.erase(gMenuItem
.action_script_id
);
9129 if (gMenuItem
.conditionId
)
9131 const PlayerCondition
* condition
= sConditionStorage
.LookupEntry
<PlayerCondition
>(gMenuItem
.conditionId
);
9134 sLog
.outErrorDb("Table gossip_menu_option for menu %u, id %u has condition_id %u that does not exist in `conditions`, ignoring", gMenuItem
.menu_id
, gMenuItem
.id
, gMenuItem
.conditionId
);
9135 gMenuItem
.conditionId
= 0;
9139 m_mGossipMenuItemsMap
.insert(GossipMenuItemsMap::value_type(gMenuItem
.menu_id
, gMenuItem
));
9144 while (result
->NextRow());
9148 if (!sLog
.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK
))
9150 for (std::set
<uint32
>::const_iterator itr
= menu_ids
.begin(); itr
!= menu_ids
.end(); ++itr
)
9151 sLog
.outErrorDb("Table `gossip_menu` contain unused (in creature or GO or menu options) menu id %u.", *itr
);
9155 sLog
.outString(">> Loaded %u gossip_menu_option entries", count
);
9158 void ObjectMgr::LoadGossipMenus()
9160 // Check which script-ids in gossip_scripts are not used
9161 std::set
<uint32
> gossipScriptSet
;
9162 for (ScriptMapMap::const_iterator itr
= sGossipScripts
.second
.begin(); itr
!= sGossipScripts
.second
.end(); ++itr
)
9163 gossipScriptSet
.insert(itr
->first
);
9165 // Load gossip_menu and gossip_menu_option data
9166 sLog
.outString("(Re)Loading Gossip menus...");
9167 LoadGossipMenu(gossipScriptSet
);
9168 sLog
.outString("(Re)Loading Gossip menu options...");
9169 LoadGossipMenuItems(gossipScriptSet
);
9171 for (std::set
<uint32
>::const_iterator itr
= gossipScriptSet
.begin(); itr
!= gossipScriptSet
.end(); ++itr
)
9172 sLog
.outErrorDb("Table `gossip_scripts` contains unused script, id %u.", *itr
);
9175 void ObjectMgr::AddVendorItem(uint32 entry
, uint32 item
, uint8 type
, uint32 maxcount
, uint32 incrtime
, uint32 extendedcost
)
9177 VendorItemData
& vList
= m_mCacheVendorItemMap
[entry
];
9178 vList
.AddItem(item
, type
, maxcount
, incrtime
, extendedcost
);
9180 WorldDatabase
.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%i','%u','%u','%u')", entry
, type
== VENDOR_ITEM_TYPE_CURRENCY
? -int32(item
) : item
, maxcount
, incrtime
, extendedcost
);
9183 bool ObjectMgr::RemoveVendorItem(uint32 entry
, uint32 item
, uint8 type
)
9185 CacheVendorItemMap::iterator iter
= m_mCacheVendorItemMap
.find(entry
);
9186 if (iter
== m_mCacheVendorItemMap
.end())
9189 if (!iter
->second
.RemoveItem(item
, type
))
9192 WorldDatabase
.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%i'", entry
, type
== VENDOR_ITEM_TYPE_CURRENCY
? -int32(item
) : item
);
9196 bool ObjectMgr::IsVendorItemValid(bool isTemplate
, char const* tableName
, uint32 vendor_entry
, uint32 item_id
, uint8 type
, uint32 maxcount
, uint32 incrtime
, uint32 ExtendedCost
, Player
* pl
, std::set
<uint32
>* skip_vendors
) const
9198 char const* idStr
= isTemplate
? "vendor template" : "vendor";
9199 char const* nameStr
= type
== VENDOR_ITEM_TYPE_CURRENCY
? "Currency" : "Item";
9200 CreatureInfo
const* cInfo
= NULL
;
9204 cInfo
= GetCreatureTemplate(vendor_entry
);
9208 ChatHandler(pl
).SendSysMessage(LANG_COMMAND_VENDORSELECTION
);
9210 sLog
.outErrorDb("Table `%s` has data for nonexistent creature (Entry: %u), ignoring", tableName
, vendor_entry
);
9214 if (!(cInfo
->npcflag
& UNIT_NPC_FLAG_VENDOR
))
9216 if (!skip_vendors
|| skip_vendors
->count(vendor_entry
) == 0)
9219 ChatHandler(pl
).SendSysMessage(LANG_COMMAND_VENDORSELECTION
);
9221 sLog
.outErrorDb("Table `%s` has data for creature (Entry: %u) without vendor flag, ignoring", tableName
, vendor_entry
);
9224 skip_vendors
->insert(vendor_entry
);
9230 CurrencyTypesEntry
const * currencyEntry
;
9231 if (type
== VENDOR_ITEM_TYPE_ITEM
)
9233 if (!GetItemPrototype(item_id
))
9236 ChatHandler(pl
).PSendSysMessage(LANG_ITEM_NOT_FOUND
, item_id
);
9238 sLog
.outErrorDb("Table `%s` for %s %u contains nonexistent item (%u), ignoring",
9239 tableName
, idStr
, vendor_entry
, item_id
);
9243 else if (type
== VENDOR_ITEM_TYPE_CURRENCY
)
9245 currencyEntry
= sCurrencyTypesStore
.LookupEntry(item_id
);
9249 ChatHandler(pl
).PSendSysMessage(LANG_CURRENCY_NOT_FOUND
, item_id
);
9251 sLog
.outErrorDb("Table `%s` for %s %u contains nonexistent currency (%u), ignoring",
9252 tableName
, idStr
, vendor_entry
, item_id
);
9257 if (currencyEntry
->ID
== CURRENCY_CONQUEST_ARENA_META
|| currencyEntry
->ID
== CURRENCY_CONQUEST_BG_META
)
9260 ChatHandler(pl
).PSendSysMessage(LANG_VENDOR_META_CURRENCY_NOT_ALLOWED
, item_id
);
9262 sLog
.outErrorDb("Table `%s` for %s %u contains not allowed meta currency (%u), ignoring",
9263 tableName
, idStr
, vendor_entry
, item_id
);
9271 ChatHandler(pl
).PSendSysMessage(LANG_VENDOR_WRONG_ITEM_TYPE
, item_id
, type
);
9273 sLog
.outErrorDb("Table `%s` for %s %u contains nonexistent vendor item type %u (entry %u), ignoring",
9274 tableName
, idStr
, vendor_entry
, type
, item_id
);
9277 if (ExtendedCost
&& !sItemExtendedCostStore
.LookupEntry(ExtendedCost
))
9280 ChatHandler(pl
).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST
, ExtendedCost
);
9282 sLog
.outErrorDb("Table `%s` contains %s (Entry: %u) with wrong ExtendedCost (%u) for %s %u, ignoring",
9283 tableName
, nameStr
, item_id
, ExtendedCost
, idStr
, vendor_entry
);
9287 if (type
== VENDOR_ITEM_TYPE_ITEM
)
9289 if (maxcount
> 0 && incrtime
== 0)
9292 ChatHandler(pl
).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount
);
9294 sLog
.outErrorDb("Table `%s` has `maxcount` (%u) for %s %u of %s %u but `incrtime`=0, ignoring",
9295 tableName
, maxcount
, nameStr
, item_id
, idStr
, vendor_entry
);
9298 else if (maxcount
== 0 && incrtime
> 0)
9301 ChatHandler(pl
).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
9303 sLog
.outErrorDb("Table `%s` has `maxcount`=0 for %s %u of %s %u but `incrtime`<>0, ignoring",
9304 tableName
, nameStr
, item_id
, idStr
, vendor_entry
);
9308 else if (type
== VENDOR_ITEM_TYPE_CURRENCY
)
9310 if (maxcount
< uint32(currencyEntry
->GetPrecision()))
9313 ChatHandler(pl
).PSendSysMessage(LANG_VENDOR_WRONG_CURRENCY_MAXCOUNT
, item_id
, uint32(currencyEntry
->GetPrecision()));
9315 sLog
.outErrorDb("Table `%s` contains %s (Entry: %u) with too low maxcount. Maxcount for currencies is buycount, so it can't be 0 or less than that's currency precision (%u), ignoring",
9316 tableName
, nameStr
, item_id
, uint32(currencyEntry
->GetPrecision()));
9321 VendorItemData
const* vItems
= isTemplate
? GetNpcVendorTemplateItemList(vendor_entry
) : GetNpcVendorItemList(vendor_entry
);
9322 VendorItemData
const* tItems
= isTemplate
? NULL
: GetNpcVendorTemplateItemList(vendor_entry
);
9324 if (!vItems
&& !tItems
)
9325 return true; // later checks for non-empty lists
9327 if (vItems
&& vItems
->FindItemCostPair(item_id
, type
, ExtendedCost
))
9330 ChatHandler(pl
).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST
, item_id
, type
== VENDOR_ITEM_TYPE_CURRENCY
, ExtendedCost
);
9332 sLog
.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u, ignoring",
9333 tableName
, nameStr
, item_id
, ExtendedCost
, idStr
, vendor_entry
);
9339 if (tItems
&& tItems
->FindItemCostPair(item_id
, type
, ExtendedCost
))
9342 ChatHandler(pl
).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST
, item_id
, type
== VENDOR_ITEM_TYPE_CURRENCY
, ExtendedCost
);
9345 if (!cInfo
->vendorId
)
9346 sLog
.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u, ignoring",
9347 tableName
, nameStr
, item_id
, ExtendedCost
, idStr
, vendor_entry
);
9349 sLog
.outErrorDb("Table `%s` has duplicate %s %u (with extended cost %u) for %s %u (or possible in vendor template %u), ignoring",
9350 tableName
, nameStr
, item_id
, ExtendedCost
, idStr
, vendor_entry
, cInfo
->vendorId
);
9356 uint32 countItems
= vItems
? vItems
->GetItemCount() : 0;
9357 countItems
+= tItems
? tItems
->GetItemCount() : 0;
9359 if (countItems
>= MAX_VENDOR_ITEMS
)
9362 ChatHandler(pl
).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS
);
9364 sLog
.outErrorDb("Table `%s` has too many entries (%u >= %i) for %s %u, ignoring",
9365 tableName
, countItems
, MAX_VENDOR_ITEMS
, idStr
, vendor_entry
);
9372 void ObjectMgr::AddGroup(Group
* group
)
9374 mGroupMap
[group
->GetId()] = group
;
9377 void ObjectMgr::RemoveGroup(Group
* group
)
9379 mGroupMap
.erase(group
->GetId());
9382 void ObjectMgr::AddArenaTeam(ArenaTeam
* arenaTeam
)
9384 mArenaTeamMap
[arenaTeam
->GetId()] = arenaTeam
;
9387 void ObjectMgr::RemoveArenaTeam(uint32 Id
)
9389 mArenaTeamMap
.erase(Id
);
9393 void ObjectMgr::GetCreatureLocaleStrings(uint32 entry
, int32 loc_idx
, char const** namePtr
, char const** subnamePtr
) const
9397 if (CreatureLocale
const* il
= GetCreatureLocale(entry
))
9399 if (namePtr
&& il
->Name
.size() > size_t(loc_idx
) && !il
->Name
[loc_idx
].empty())
9400 *namePtr
= il
->Name
[loc_idx
].c_str();
9402 if (subnamePtr
&& il
->SubName
.size() > size_t(loc_idx
) && !il
->SubName
[loc_idx
].empty())
9403 *subnamePtr
= il
->SubName
[loc_idx
].c_str();
9408 void ObjectMgr::GetItemLocaleStrings(uint32 entry
, int32 loc_idx
, std::string
* namePtr
, std::string
* descriptionPtr
) const
9412 if (ItemLocale
const* il
= GetItemLocale(entry
))
9414 if (namePtr
&& il
->Name
.size() > size_t(loc_idx
) && !il
->Name
[loc_idx
].empty())
9415 *namePtr
= il
->Name
[loc_idx
];
9417 if (descriptionPtr
&& il
->Description
.size() > size_t(loc_idx
) && !il
->Description
[loc_idx
].empty())
9418 *descriptionPtr
= il
->Description
[loc_idx
];
9423 void ObjectMgr::GetQuestLocaleStrings(uint32 entry
, int32 loc_idx
, std::string
* titlePtr
) const
9427 if (QuestLocale
const* il
= GetQuestLocale(entry
))
9429 if (titlePtr
&& il
->Title
.size() > size_t(loc_idx
) && !il
->Title
[loc_idx
].empty())
9430 *titlePtr
= il
->Title
[loc_idx
];
9435 void ObjectMgr::GetNpcTextLocaleStringsAll(uint32 entry
, int32 loc_idx
, ObjectMgr::NpcTextArray
* text0_Ptr
, ObjectMgr::NpcTextArray
* text1_Ptr
) const
9439 if (NpcTextLocale
const* nl
= GetNpcTextLocale(entry
))
9442 for (int i
= 0; i
< MAX_GOSSIP_TEXT_OPTIONS
; ++i
)
9443 if (nl
->Text_0
[i
].size() > (size_t)loc_idx
&& !nl
->Text_0
[i
][loc_idx
].empty())
9444 (*text0_Ptr
)[i
] = nl
->Text_0
[i
][loc_idx
];
9447 for (int i
= 0; i
< MAX_GOSSIP_TEXT_OPTIONS
; ++i
)
9448 if (nl
->Text_1
[i
].size() > (size_t)loc_idx
&& !nl
->Text_1
[i
][loc_idx
].empty())
9449 (*text1_Ptr
)[i
] = nl
->Text_1
[i
][loc_idx
];
9454 void ObjectMgr::GetNpcTextLocaleStrings0(uint32 entry
, int32 loc_idx
, std::string
* text0_0_Ptr
, std::string
* text1_0_Ptr
) const
9458 if (NpcTextLocale
const* nl
= GetNpcTextLocale(entry
))
9461 if (nl
->Text_0
[0].size() > (size_t)loc_idx
&& !nl
->Text_0
[0][loc_idx
].empty())
9462 *text0_0_Ptr
= nl
->Text_0
[0][loc_idx
];
9465 if (nl
->Text_1
[0].size() > (size_t)loc_idx
&& !nl
->Text_1
[0][loc_idx
].empty())
9466 *text1_0_Ptr
= nl
->Text_1
[0][loc_idx
];
9471 // Functions for scripting access
9472 bool LoadMangosStrings(DatabaseType
& db
, char const* table
, int32 start_value
, int32 end_value
)
9474 // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
9475 // start/end reversed for negative values
9476 if (start_value
> MAX_DB_SCRIPT_STRING_ID
|| end_value
>= start_value
)
9478 sLog
.outErrorDb("Table '%s' attempt loaded with reserved by mangos range (%d - %d), strings not loaded.", table
, start_value
, end_value
+ 1);
9482 return sObjectMgr
.LoadMangosStrings(db
, table
, start_value
, end_value
);
9485 void ObjectMgr::LoadCreatureTemplateSpells()
9487 sCreatureTemplateSpellsStorage
.Load();
9489 sLog
.outString(">> Loaded %u creature_template_spells definitions", sCreatureTemplateSpellsStorage
.GetRecordCount());
9492 for (SQLStorageBase::SQLSIterator
<CreatureTemplateSpells
> itr
= sCreatureTemplateSpellsStorage
.getDataBegin
<CreatureTemplateSpells
>(); itr
< sCreatureTemplateSpellsStorage
.getDataEnd
<CreatureTemplateSpells
>(); ++itr
)
9494 if (!sCreatureStorage
.LookupEntry
<CreatureInfo
>(itr
->entry
))
9496 sLog
.outErrorDb("LoadCreatureTemplateSpells: Spells found for creature entry %u, but creature does not exist, skipping", itr
->entry
);
9497 sCreatureTemplateSpellsStorage
.EraseEntry(itr
->entry
);
9499 for (uint8 i
= 0; i
< CREATURE_MAX_SPELLS
; ++i
)
9501 if (itr
->spells
[i
] && !sSpellStore
.LookupEntry(itr
->spells
[i
]))
9503 sLog
.outErrorDb("LoadCreatureTemplateSpells: Spells found for creature entry %u, assigned spell %u does not exist, set to 0", itr
->entry
, itr
->spells
[i
]);
9504 const_cast<CreatureTemplateSpells
*>(*itr
)->spells
[i
] = 0;
9510 CreatureInfo
const* GetCreatureTemplateStore(uint32 entry
)
9512 return sCreatureStorage
.LookupEntry
<CreatureInfo
>(entry
);
9515 Quest
const* GetQuestTemplateStore(uint32 entry
)
9517 return sObjectMgr
.GetQuestTemplate(entry
);
9520 bool FindCreatureData::operator()(CreatureDataPair
const& dataPair
)
9522 // skip wrong entry ids
9523 if (i_id
&& dataPair
.second
.id
!= i_id
)
9527 i_anyData
= &dataPair
;
9529 // without player we can't find more stricted cases, so use fouded
9533 // skip diff. map cases
9534 if (dataPair
.second
.mapid
!= i_player
->GetMapId())
9537 float new_dist
= i_player
->GetDistance2d(dataPair
.second
.posX
, dataPair
.second
.posY
);
9539 if (!i_mapData
|| new_dist
< i_mapDist
)
9541 i_mapData
= &dataPair
;
9542 i_mapDist
= new_dist
;
9545 // skip not spawned (in any state),
9546 uint16 pool_id
= sPoolMgr
.IsPartOfAPool
<Creature
>(dataPair
.first
);
9547 if (pool_id
&& !i_player
->GetMap()->GetPersistentState()->IsSpawnedPoolObject
<Creature
>(dataPair
.first
))
9550 if (!i_spawnedData
|| new_dist
< i_spawnedDist
)
9552 i_spawnedData
= &dataPair
;
9553 i_spawnedDist
= new_dist
;
9559 CreatureDataPair
const* FindCreatureData::GetResult() const
9562 return i_spawnedData
;
9570 bool FindGOData::operator()(GameObjectDataPair
const& dataPair
)
9572 // skip wrong entry ids
9573 if (i_id
&& dataPair
.second
.id
!= i_id
)
9577 i_anyData
= &dataPair
;
9579 // without player we can't find more stricted cases, so use fouded
9583 // skip diff. map cases
9584 if (dataPair
.second
.mapid
!= i_player
->GetMapId())
9587 float new_dist
= i_player
->GetDistance2d(dataPair
.second
.posX
, dataPair
.second
.posY
);
9589 if (!i_mapData
|| new_dist
< i_mapDist
)
9591 i_mapData
= &dataPair
;
9592 i_mapDist
= new_dist
;
9595 // skip not spawned (in any state)
9596 uint16 pool_id
= sPoolMgr
.IsPartOfAPool
<GameObject
>(dataPair
.first
);
9597 if (pool_id
&& !i_player
->GetMap()->GetPersistentState()->IsSpawnedPoolObject
<GameObject
>(dataPair
.first
))
9600 if (!i_spawnedData
|| new_dist
< i_spawnedDist
)
9602 i_spawnedData
= &dataPair
;
9603 i_spawnedDist
= new_dist
;
9609 GameObjectDataPair
const* FindGOData::GetResult() const
9615 return i_spawnedData
;