[8449] Deprecate healing/damage item mods and merge internal data in to spell power.
[getmangos.git] / src / game / ObjectMgr.cpp
blobd6a70f0d881b8e043af6320765a0af10d7a31d48
1 /*
2 * Copyright (C) 2005-2009 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 "Common.h"
20 #include "Database/DatabaseEnv.h"
21 #include "Database/SQLStorage.h"
22 #include "Database/SQLStorageImpl.h"
23 #include "Policies/SingletonImp.h"
25 #include "Log.h"
26 #include "MapManager.h"
27 #include "ObjectMgr.h"
28 #include "SpellMgr.h"
29 #include "UpdateMask.h"
30 #include "World.h"
31 #include "Group.h"
32 #include "Guild.h"
33 #include "ArenaTeam.h"
34 #include "Transports.h"
35 #include "ProgressBar.h"
36 #include "Language.h"
37 #include "GameEventMgr.h"
38 #include "Spell.h"
39 #include "Chat.h"
40 #include "AccountMgr.h"
41 #include "InstanceSaveMgr.h"
42 #include "SpellAuras.h"
43 #include "Util.h"
44 #include "WaypointManager.h"
46 INSTANTIATE_SINGLETON_1(ObjectMgr);
48 ScriptMapMap sQuestEndScripts;
49 ScriptMapMap sQuestStartScripts;
50 ScriptMapMap sSpellScripts;
51 ScriptMapMap sGameObjectScripts;
52 ScriptMapMap sEventScripts;
54 bool normalizePlayerName(std::string& name)
56 if(name.empty())
57 return false;
59 wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
60 size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
62 if(!Utf8toWStr(name,&wstr_buf[0],wstr_len))
63 return false;
65 wstr_buf[0] = wcharToUpper(wstr_buf[0]);
66 for(size_t i = 1; i < wstr_len; ++i)
67 wstr_buf[i] = wcharToLower(wstr_buf[i]);
69 if(!WStrToUtf8(wstr_buf,wstr_len,name))
70 return false;
72 return true;
75 LanguageDesc lang_description[LANGUAGES_COUNT] =
77 { LANG_ADDON, 0, 0 },
78 { LANG_UNIVERSAL, 0, 0 },
79 { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
80 { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
81 { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
82 { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
83 { LANG_COMMON, 668, SKILL_LANG_COMMON },
84 { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
85 { LANG_TITAN, 816, SKILL_LANG_TITAN },
86 { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
87 { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
88 { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
89 { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
90 { LANG_TROLL, 7341, SKILL_LANG_TROLL },
91 { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
92 { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
93 { LANG_ZOMBIE, 0, 0 },
94 { LANG_GNOMISH_BINARY, 0, 0 },
95 { LANG_GOBLIN_BINARY, 0, 0 }
98 LanguageDesc const* GetLanguageDescByID(uint32 lang)
100 for(int i = 0; i < LANGUAGES_COUNT; ++i)
102 if(uint32(lang_description[i].lang_id) == lang)
103 return &lang_description[i];
106 return NULL;
109 bool SpellClickInfo::IsFitToRequirements(Player const* player) const
111 if(questStart)
113 // not in expected required quest state
114 if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart))
115 return false;
118 if(questEnd)
120 // not in expected forbidden quest state
121 if(!player || player->GetQuestRewardStatus(questEnd))
122 return false;
125 return true;
128 ObjectMgr::ObjectMgr()
130 m_hiCharGuid = 1;
131 m_hiCreatureGuid = 1;
132 m_hiPetGuid = 1;
133 m_hiVehicleGuid = 1;
134 m_hiItemGuid = 1;
135 m_hiGoGuid = 1;
136 m_hiDoGuid = 1;
137 m_hiCorpseGuid = 1;
138 m_hiPetNumber = 1;
139 m_ItemTextId = 1;
140 m_mailid = 1;
141 m_equipmentSetGuid = 1;
142 m_guildId = 1;
143 m_arenaTeamId = 1;
144 m_auctionid = 1;
146 // Only zero condition left, others will be added while loading DB tables
147 mConditions.resize(1);
150 ObjectMgr::~ObjectMgr()
152 for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++i )
153 delete i->second;
155 for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++i )
156 delete[] i->second;
158 // free only if loaded
159 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
160 delete[] playerClassInfo[class_].levelInfo;
162 for (int race = 0; race < MAX_RACES; ++race)
163 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
164 delete[] playerInfo[race][class_].levelInfo;
166 // free group and guild objects
167 for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
168 delete (*itr);
170 for (GuildMap::iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
171 delete itr->second;
173 for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
174 delete itr->second;
176 for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
177 itr->second.Clear();
179 for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
180 itr->second.Clear();
183 Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const
185 for(GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
186 if ((*itr)->GetLeaderGUID() == guid)
187 return *itr;
189 return NULL;
192 Guild * ObjectMgr::GetGuildById(uint32 GuildId) const
194 GuildMap::const_iterator itr = mGuildMap.find(GuildId);
195 if (itr != mGuildMap.end())
196 return itr->second;
198 return NULL;
201 Guild * ObjectMgr::GetGuildByName(const std::string& guildname) const
203 for(GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
204 if (itr->second->GetName() == guildname)
205 return itr->second;
207 return NULL;
210 std::string ObjectMgr::GetGuildNameById(uint32 GuildId) const
212 GuildMap::const_iterator itr = mGuildMap.find(GuildId);
213 if (itr != mGuildMap.end())
214 return itr->second->GetName();
216 return "";
219 Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
221 for(GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
222 if (itr->second->GetLeader() == guid)
223 return itr->second;
225 return NULL;
228 void ObjectMgr::AddGuild(Guild* guild)
230 mGuildMap[guild->GetId()] = guild;
233 void ObjectMgr::RemoveGuild(uint32 Id)
235 mGuildMap.erase(Id);
238 ArenaTeam* ObjectMgr::GetArenaTeamById(uint32 arenateamid) const
240 ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid);
241 if (itr != mArenaTeamMap.end())
242 return itr->second;
244 return NULL;
247 ArenaTeam* ObjectMgr::GetArenaTeamByName(const std::string& arenateamname) const
249 for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
250 if (itr->second->GetName() == arenateamname)
251 return itr->second;
253 return NULL;
256 ArenaTeam* ObjectMgr::GetArenaTeamByCaptain(uint64 const& guid) const
258 for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
259 if (itr->second->GetCaptain() == guid)
260 return itr->second;
262 return NULL;
265 void ObjectMgr::AddArenaTeam(ArenaTeam* arenaTeam)
267 mArenaTeamMap[arenaTeam->GetId()] = arenaTeam;
270 void ObjectMgr::RemoveArenaTeam(uint32 Id)
272 mArenaTeamMap.erase(Id);
275 CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
277 return sCreatureStorage.LookupEntry<CreatureInfo>(id);
280 void ObjectMgr::LoadCreatureLocales()
282 mCreatureLocaleMap.clear(); // need for reload case
284 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");
286 if(!result)
288 barGoLink bar(1);
290 bar.step();
292 sLog.outString();
293 sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
294 return;
297 barGoLink bar(result->GetRowCount());
301 Field *fields = result->Fetch();
302 bar.step();
304 uint32 entry = fields[0].GetUInt32();
306 CreatureLocale& data = mCreatureLocaleMap[entry];
308 for(int i = 1; i < MAX_LOCALE; ++i)
310 std::string str = fields[1+2*(i-1)].GetCppString();
311 if(!str.empty())
313 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
314 if(idx >= 0)
316 if(data.Name.size() <= idx)
317 data.Name.resize(idx+1);
319 data.Name[idx] = str;
322 str = fields[1+2*(i-1)+1].GetCppString();
323 if(!str.empty())
325 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
326 if(idx >= 0)
328 if(data.SubName.size() <= idx)
329 data.SubName.resize(idx+1);
331 data.SubName[idx] = str;
335 } while (result->NextRow());
337 delete result;
339 sLog.outString();
340 sLog.outString( ">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size() );
343 void ObjectMgr::LoadNpcOptionLocales()
345 mNpcOptionLocaleMap.clear(); // need for reload case
347 QueryResult *result = WorldDatabase.Query("SELECT entry,"
348 "option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
349 "option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
350 "option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
351 "option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
352 "FROM locales_npc_option");
354 if(!result)
356 barGoLink bar(1);
358 bar.step();
360 sLog.outString();
361 sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty.");
362 return;
365 barGoLink bar(result->GetRowCount());
369 Field *fields = result->Fetch();
370 bar.step();
372 uint32 entry = fields[0].GetUInt32();
374 NpcOptionLocale& data = mNpcOptionLocaleMap[entry];
376 for(int i = 1; i < MAX_LOCALE; ++i)
378 std::string str = fields[1+2*(i-1)].GetCppString();
379 if(!str.empty())
381 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
382 if(idx >= 0)
384 if(data.OptionText.size() <= idx)
385 data.OptionText.resize(idx+1);
387 data.OptionText[idx] = str;
390 str = fields[1+2*(i-1)+1].GetCppString();
391 if(!str.empty())
393 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
394 if(idx >= 0)
396 if(data.BoxText.size() <= idx)
397 data.BoxText.resize(idx+1);
399 data.BoxText[idx] = str;
403 } while (result->NextRow());
405 delete result;
407 sLog.outString();
408 sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() );
411 void ObjectMgr::LoadPointOfInterestLocales()
413 mPointOfInterestLocaleMap.clear(); // need for reload case
415 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");
417 if(!result)
419 barGoLink bar(1);
421 bar.step();
423 sLog.outString();
424 sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty.");
425 return;
428 barGoLink bar(result->GetRowCount());
432 Field *fields = result->Fetch();
433 bar.step();
435 uint32 entry = fields[0].GetUInt32();
437 PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry];
439 for(int i = 1; i < MAX_LOCALE; ++i)
441 std::string str = fields[i].GetCppString();
442 if(str.empty())
443 continue;
445 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
446 if(idx >= 0)
448 if(data.IconName.size() <= idx)
449 data.IconName.resize(idx+1);
451 data.IconName[idx] = str;
454 } while (result->NextRow());
456 delete result;
458 sLog.outString();
459 sLog.outString( ">> Loaded %lu points_of_interest locale strings", (unsigned long)mPointOfInterestLocaleMap.size() );
462 struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader>
464 template<class D>
465 void convert_from_str(uint32 /*field_pos*/, char *src, D &dst)
467 dst = D(objmgr.GetScriptId(src));
471 void ObjectMgr::LoadCreatureTemplates()
473 SQLCreatureLoader loader;
474 loader.Load(sCreatureStorage);
476 sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
477 sLog.outString();
479 std::set<uint32> heroicEntries; // already loaded heroic value in creatures
480 std::set<uint32> hasHeroicEntries; // already loaded creatures with heroic entry values
482 // check data correctness
483 for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
485 CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
486 if (!cInfo)
487 continue;
489 if (cInfo->HeroicEntry)
491 CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
492 if (!heroicInfo)
494 sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.", i, cInfo->HeroicEntry, cInfo->HeroicEntry);
495 continue;
498 if (heroicEntries.find(i)!=heroicEntries.end())
500 sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i);
501 continue;
504 if (heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end())
506 sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry);
507 continue;
510 if (hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end())
512 sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry);
513 continue;
516 if (cInfo->unit_class != heroicInfo->unit_class)
518 sLog.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in heroic mode (Entry: %u, class %u).",i, cInfo->unit_class, cInfo->HeroicEntry, heroicInfo->unit_class);
519 continue;
522 if (cInfo->npcflag != heroicInfo->npcflag)
524 sLog.outErrorDb("Creature (Entry: %u) has different `npcflag` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
525 continue;
528 if (cInfo->trainer_class != heroicInfo->trainer_class)
530 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_class` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
531 continue;
534 if (cInfo->trainer_race != heroicInfo->trainer_race)
536 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_race` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
537 continue;
540 if (cInfo->trainer_type != heroicInfo->trainer_type)
542 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_type` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
543 continue;
546 if (cInfo->trainer_spell != heroicInfo->trainer_spell)
548 sLog.outErrorDb("Creature (Entry: %u) has different `trainer_spell` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
549 continue;
552 if (heroicInfo->AIName && *heroicInfo->AIName)
554 sLog.outErrorDb("Heroic mode creature (Entry: %u) has `AIName`, but in any case will used normal mode creature (Entry: %u) AIName.",cInfo->HeroicEntry,i);
555 continue;
558 if (heroicInfo->ScriptID)
560 sLog.outErrorDb("Heroic mode creature (Entry: %u) has `ScriptName`, but in any case will used normal mode creature (Entry: %u) ScriptName.",cInfo->HeroicEntry,i);
561 continue;
564 hasHeroicEntries.insert(i);
565 heroicEntries.insert(cInfo->HeroicEntry);
568 FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
569 if (!factionTemplate)
570 sLog.outErrorDb("Creature (Entry: %u) has non-existing 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 non-existing faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
576 // used later for scale
577 CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
579 if (cInfo->DisplayID_A[0])
581 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[0]);
582 if(!displayEntry)
584 sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_A id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[0]);
585 const_cast<CreatureInfo*>(cInfo)->DisplayID_A[0] = 0;
587 else if(!displayScaleEntry)
588 displayScaleEntry = displayEntry;
590 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A[0]);
591 if (!minfo)
592 sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_A (%u)", cInfo->Entry, cInfo->DisplayID_A[0]);
595 if (cInfo->DisplayID_A[1])
597 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[1]);
598 if(!displayEntry)
600 sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_A2 id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[1]);
601 const_cast<CreatureInfo*>(cInfo)->DisplayID_A[1] = 0;
603 else if(!displayScaleEntry)
604 displayScaleEntry = displayEntry;
606 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A[1]);
607 if (!minfo)
608 sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_A2 (%u)", cInfo->Entry, cInfo->DisplayID_A[1]);
611 if (cInfo->DisplayID_H[0])
613 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[0]);
614 if(!displayEntry)
616 sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_H id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[0]);
617 const_cast<CreatureInfo*>(cInfo)->DisplayID_H[0] = 0;
619 else if(!displayScaleEntry)
620 displayScaleEntry = displayEntry;
622 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H[0]);
623 if (!minfo)
624 sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_H (%u)", cInfo->Entry, cInfo->DisplayID_H[0]);
627 if (cInfo->DisplayID_H[1])
629 CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[1]);
630 if(!displayEntry)
632 sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_H2 id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[1]);
633 const_cast<CreatureInfo*>(cInfo)->DisplayID_H[1] = 0;
635 else if(!displayScaleEntry)
636 displayScaleEntry = displayEntry;
638 CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H[1]);
639 if (!minfo)
640 sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_H2 (%u)", cInfo->Entry, cInfo->DisplayID_H[1]);
643 if (!displayScaleEntry)
644 sLog.outErrorDb("Creature (Entry: %u) not has any existed display id in DisplayID_A/DisplayID_A2/DisplayID_H/DisplayID_H2", cInfo->Entry);
646 for(int k = 0; k < MAX_KILL_CREDIT; ++k)
648 if(cInfo->KillCredit[k])
650 if(!GetCreatureTemplate(cInfo->KillCredit[k]))
652 sLog.outErrorDb("Creature (Entry: %u) has not existed creature entry in `KillCredit%d` (%u)",cInfo->Entry,k+1,cInfo->KillCredit[k]);
653 const_cast<CreatureInfo*>(cInfo)->KillCredit[k] = 0;
658 if (cInfo->unit_class && ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
659 sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class);
661 if(cInfo->dmgschool >= MAX_SPELL_SCHOOL)
663 sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool);
664 const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
667 if(cInfo->baseattacktime == 0)
668 const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
670 if(cInfo->rangeattacktime == 0)
671 const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
673 if(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK)
675 sLog.outErrorDb("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it expect to be set by code base at `npc_spellclick_spells` content.",cInfo->Entry,UNIT_NPC_FLAG_SPELLCLICK);
676 const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
679 if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
680 sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type);
682 if(cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
684 sLog.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`",cInfo->Entry,cInfo->type);
685 const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
688 // must exist or used hidden but used in data horse case
689 if(cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM )
691 sLog.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`",cInfo->Entry,cInfo->family);
692 const_cast<CreatureInfo*>(cInfo)->family = 0;
695 if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
697 sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType);
698 const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
701 if(cInfo->PetSpellDataId)
703 CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
704 if(!spellDataId)
705 sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
708 for(int j = 0; j < CREATURE_MAX_SPELLS; ++j)
710 if(cInfo->spells[j] && !sSpellStore.LookupEntry(cInfo->spells[j]))
712 sLog.outErrorDb("Creature (Entry: %u) has non-existing Spell%d (%u), set to 0", cInfo->Entry, j+1,cInfo->spells[j]);
713 const_cast<CreatureInfo*>(cInfo)->spells[j] = 0;
717 if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
719 sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
720 const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
723 if(cInfo->equipmentId > 0) // 0 no equipment
725 if(!GetEquipmentInfo(cInfo->equipmentId))
727 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);
728 const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
732 /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
733 if(cInfo->scale <= 0.0f)
735 if(displayScaleEntry)
736 const_cast<CreatureInfo*>(cInfo)->scale = displayScaleEntry->scale;
737 else
738 const_cast<CreatureInfo*>(cInfo)->scale = 1.0f;
743 void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
745 // Now add the auras, format "spellid effectindex spellid effectindex..."
746 char *p,*s;
747 std::vector<int> val;
748 s=p=(char*)reinterpret_cast<char const*>(addon->auras);
749 if(p)
751 while (p[0]!=0)
753 ++p;
754 if (p[0]==' ')
756 val.push_back(atoi(s));
757 s=++p;
760 if (p!=s)
761 val.push_back(atoi(s));
763 // free char* loaded memory
764 delete[] (char*)reinterpret_cast<char const*>(addon->auras);
766 // wrong list
767 if (val.size()%2)
769 addon->auras = NULL;
770 sLog.outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`.",guidEntryStr,addon->guidOrEntry,table);
771 return;
775 // empty list
776 if(val.empty())
778 addon->auras = NULL;
779 return;
782 // replace by new structures array
783 const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
785 int i=0;
786 for(int j=0;j<val.size()/2;++j)
788 CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
789 cAura.spell_id = (uint32)val[2*j+0];
790 cAura.effect_idx = (uint32)val[2*j+1];
791 if ( cAura.effect_idx > 2 )
793 sLog.outErrorDb("Creature (%s: %u) has wrong effect %u for spell %u in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
794 continue;
796 SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
797 if (!AdditionalSpellInfo)
799 sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.spell_id,table);
800 continue;
803 if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
805 sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
806 continue;
809 ++i;
812 // fill terminator element (after last added)
813 CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
814 endAura.spell_id = 0;
815 endAura.effect_idx = 0;
818 void ObjectMgr::LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment)
820 creatureaddons.Load();
822 sLog.outString(">> Loaded %u %s", creatureaddons.RecordCount, comment);
823 sLog.outString();
825 // check data correctness and convert 'auras'
826 for(uint32 i = 1; i < creatureaddons.MaxEntry; ++i)
828 CreatureDataAddon const* addon = creatureaddons.LookupEntry<CreatureDataAddon>(i);
829 if(!addon)
830 continue;
832 if (addon->mount)
834 if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount))
836 sLog.outErrorDb("Creature (%s %u) have invalid displayInfoId for mount (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->mount, creatureaddons.GetTableName());
837 const_cast<CreatureDataAddon*>(addon)->mount = 0;
841 if (!sEmotesStore.LookupEntry(addon->emote))
842 sLog.outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->emote, creatureaddons.GetTableName());
844 if (addon->move_flags & (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4))
846 sLog.outErrorDb("Creature (%s %u) movement flags mask defined in `%s` include forbidden flags (" I32FMT ") that can crash client, cleanup at load.", entryName, addon->guidOrEntry, creatureaddons.GetTableName(), (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4));
847 const_cast<CreatureDataAddon*>(addon)->move_flags &= ~(MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4);
850 ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), creatureaddons.GetTableName(), entryName);
854 void ObjectMgr::LoadCreatureAddons()
856 LoadCreatureAddons(sCreatureInfoAddonStorage,"Entry","creature template addons");
858 // check entry ids
859 for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
860 if(CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i))
861 if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
862 sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`",addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName());
864 LoadCreatureAddons(sCreatureDataAddonStorage,"GUID","creature addons");
866 // check entry ids
867 for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
868 if(CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i))
869 if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end())
870 sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry);
873 EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
875 return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
878 void ObjectMgr::LoadEquipmentTemplates()
880 sEquipmentStorage.Load();
882 for(uint32 i=0; i< sEquipmentStorage.MaxEntry; ++i)
884 EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i);
886 if(!eqInfo)
887 continue;
889 for(uint8 j=0; j<3; j++)
891 if(!eqInfo->equipentry[j])
892 continue;
894 ItemEntry const *dbcitem = sItemStore.LookupEntry(eqInfo->equipentry[j]);
896 if(!dbcitem)
898 sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j+1, i);
899 const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
900 continue;
903 if(dbcitem->InventoryType != INVTYPE_WEAPON &&
904 dbcitem->InventoryType != INVTYPE_SHIELD &&
905 dbcitem->InventoryType != INVTYPE_RANGED &&
906 dbcitem->InventoryType != INVTYPE_2HWEAPON &&
907 dbcitem->InventoryType != INVTYPE_WEAPONMAINHAND &&
908 dbcitem->InventoryType != INVTYPE_WEAPONOFFHAND &&
909 dbcitem->InventoryType != INVTYPE_HOLDABLE &&
910 dbcitem->InventoryType != INVTYPE_THROWN &&
911 dbcitem->InventoryType != INVTYPE_RANGEDRIGHT)
913 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);
914 const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
918 sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
919 sLog.outString();
922 CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
924 return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
927 uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data /*= NULL*/)
929 // Load creature model (display id)
930 if (data && data->displayid)
931 return data->displayid;
933 // use defaults from the template
934 uint32 display_id;
936 // DisplayID_A is used if no team is given
937 if (team == HORDE)
939 if(cinfo->DisplayID_H[0])
940 display_id = cinfo->DisplayID_H[1] ? cinfo->DisplayID_H[urand(0,1)] : cinfo->DisplayID_H[0];
941 else
942 display_id = cinfo->DisplayID_H[1];
944 if(!display_id)
945 display_id = cinfo->DisplayID_A[0] ? cinfo->DisplayID_A[0] : cinfo->DisplayID_A[1];
947 else
949 if(cinfo->DisplayID_A[0])
950 display_id = cinfo->DisplayID_A[1] ? cinfo->DisplayID_A[urand(0,1)] : cinfo->DisplayID_A[0];
951 else
952 display_id = cinfo->DisplayID_A[1];
954 if(!display_id)
955 display_id = cinfo->DisplayID_H[0] ? cinfo->DisplayID_H[0] : cinfo->DisplayID_H[1];
958 return display_id;
961 CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
963 CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
964 if(!minfo)
965 return NULL;
967 // If a model for another gender exists, 50% chance to use it
968 if(minfo->modelid_other_gender != 0 && urand(0,1) == 0)
970 CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
971 if(!minfo_tmp)
973 sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
974 return minfo; // not fatal, just use the previous one
976 else
977 return minfo_tmp;
979 else
980 return minfo;
983 void ObjectMgr::LoadCreatureModelInfo()
985 sCreatureModelStorage.Load();
987 // post processing
988 for(uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i)
990 CreatureModelInfo const *minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
991 if (!minfo)
992 continue;
994 if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid))
995 sLog.outErrorDb("Table `creature_model_info` has model for not existed display id (%u).", minfo->modelid);
997 if (minfo->gender > GENDER_NONE)
999 sLog.outErrorDb("Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(minfo->gender), minfo->modelid);
1000 const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE;
1003 if (minfo->modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender))
1005 sLog.outErrorDb("Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", minfo->modelid_other_gender, minfo->modelid);
1006 const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
1010 sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount );
1011 sLog.outString();
1014 void ObjectMgr::LoadCreatures()
1016 uint32 count = 0;
1017 // 0 1 2 3
1018 QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid,"
1019 // 4 5 6 7 8 9 10 11
1020 "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
1021 // 12 13 14 15 16 17 18 19
1022 "curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event, pool_entry "
1023 "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
1024 "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");
1026 if(!result)
1028 barGoLink bar(1);
1030 bar.step();
1032 sLog.outString();
1033 sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
1034 return;
1037 // build single time for check creature data
1038 std::set<uint32> heroicCreatures;
1039 for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
1040 if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
1041 if(cInfo->HeroicEntry)
1042 heroicCreatures.insert(cInfo->HeroicEntry);
1044 barGoLink bar(result->GetRowCount());
1048 Field *fields = result->Fetch();
1049 bar.step();
1051 uint32 guid = fields[ 0].GetUInt32();
1052 uint32 entry = fields[ 1].GetUInt32();
1054 CreatureInfo const* cInfo = GetCreatureTemplate(entry);
1055 if(!cInfo)
1057 sLog.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
1058 continue;
1061 CreatureData& data = mCreatureDataMap[guid];
1063 data.id = entry;
1064 data.mapid = fields[ 2].GetUInt32();
1065 data.displayid = fields[ 3].GetUInt32();
1066 data.equipmentId = fields[ 4].GetUInt32();
1067 data.posX = fields[ 5].GetFloat();
1068 data.posY = fields[ 6].GetFloat();
1069 data.posZ = fields[ 7].GetFloat();
1070 data.orientation = fields[ 8].GetFloat();
1071 data.spawntimesecs = fields[ 9].GetUInt32();
1072 data.spawndist = fields[10].GetFloat();
1073 data.currentwaypoint= fields[11].GetUInt32();
1074 data.curhealth = fields[12].GetUInt32();
1075 data.curmana = fields[13].GetUInt32();
1076 data.is_dead = fields[14].GetBool();
1077 data.movementType = fields[15].GetUInt8();
1078 data.spawnMask = fields[16].GetUInt8();
1079 data.phaseMask = fields[17].GetUInt16();
1080 int16 gameEvent = fields[18].GetInt16();
1081 int16 PoolId = fields[19].GetInt16();
1083 if(heroicCreatures.find(data.id)!=heroicCreatures.end())
1085 sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template`, skipped.",guid,data.id );
1086 continue;
1089 if(data.equipmentId > 0) // -1 no equipment, 0 use default
1091 if(!GetEquipmentInfo(data.equipmentId))
1093 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);
1094 data.equipmentId = -1;
1098 if(cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
1100 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 );
1101 data.curhealth = cInfo->minhealth;
1104 if(cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
1106 MapEntry const* map = sMapStore.LookupEntry(data.mapid);
1107 if(!map || !map->IsDungeon())
1108 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",guid,data.id);
1111 if(data.curmana < cInfo->minmana)
1113 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 );
1114 data.curmana = cInfo->minmana;
1117 if(data.spawndist < 0.0f)
1119 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.",guid,data.id );
1120 data.spawndist = 0.0f;
1122 else if(data.movementType == RANDOM_MOTION_TYPE)
1124 if(data.spawndist == 0.0f)
1126 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 );
1127 data.movementType = IDLE_MOTION_TYPE;
1130 else if(data.movementType == IDLE_MOTION_TYPE)
1132 if(data.spawndist != 0.0f)
1134 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.",guid,data.id );
1135 data.spawndist = 0.0f;
1139 if(data.phaseMask==0)
1141 sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id );
1142 data.phaseMask = 1;
1145 if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system
1146 AddCreatureToGrid(guid, &data);
1148 ++count;
1150 } while (result->NextRow());
1152 delete result;
1154 sLog.outString();
1155 sLog.outString( ">> Loaded %lu creatures", (unsigned long)mCreatureDataMap.size() );
1158 void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
1160 uint8 mask = data->spawnMask;
1161 for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1163 if(mask & 1)
1165 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1166 uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1168 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1169 cell_guids.creatures.insert(guid);
1174 void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
1176 uint8 mask = data->spawnMask;
1177 for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1179 if(mask & 1)
1181 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1182 uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1184 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1185 cell_guids.creatures.erase(guid);
1190 void ObjectMgr::LoadGameobjects()
1192 uint32 count = 0;
1194 // 0 1 2 3 4 5 6
1195 QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation,"
1196 // 7 8 9 10 11 12 13 14 15 16 17
1197 "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event, pool_entry "
1198 "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
1199 "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid");
1201 if(!result)
1203 barGoLink bar(1);
1205 bar.step();
1207 sLog.outString();
1208 sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
1209 return;
1212 barGoLink bar(result->GetRowCount());
1216 Field *fields = result->Fetch();
1217 bar.step();
1219 uint32 guid = fields[ 0].GetUInt32();
1220 uint32 entry = fields[ 1].GetUInt32();
1222 GameObjectInfo const* gInfo = GetGameObjectInfo(entry);
1223 if (!gInfo)
1225 sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
1226 continue;
1229 if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
1231 sLog.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.",guid, entry, gInfo->type, gInfo->displayId);
1232 continue;
1235 GameObjectData& data = mGameObjectDataMap[guid];
1237 data.id = entry;
1238 data.mapid = fields[ 2].GetUInt32();
1239 data.posX = fields[ 3].GetFloat();
1240 data.posY = fields[ 4].GetFloat();
1241 data.posZ = fields[ 5].GetFloat();
1242 data.orientation = fields[ 6].GetFloat();
1243 data.rotation0 = fields[ 7].GetFloat();
1244 data.rotation1 = fields[ 8].GetFloat();
1245 data.rotation2 = fields[ 9].GetFloat();
1246 data.rotation3 = fields[10].GetFloat();
1247 data.spawntimesecs = fields[11].GetInt32();
1249 if (data.spawntimesecs==0 && gInfo->IsDespawnAtAction())
1251 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.",guid,data.id);
1254 data.animprogress = fields[12].GetUInt32();
1256 uint32 go_state = fields[13].GetUInt32();
1257 if (go_state >= MAX_GO_STATE)
1259 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip",guid,data.id,go_state);
1260 continue;
1262 data.go_state = GOState(go_state);
1264 data.spawnMask = fields[14].GetUInt8();
1265 data.phaseMask = fields[15].GetUInt16();
1266 int16 gameEvent = fields[16].GetInt16();
1267 int16 PoolId = fields[17].GetInt16();
1269 if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
1271 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip",guid,data.id,data.rotation2 );
1272 continue;
1275 if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
1277 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip",guid,data.id,data.rotation3 );
1278 continue;
1281 if (!MapManager::IsValidMapCoord(data.mapid,data.posX,data.posY,data.posZ,data.orientation))
1283 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip",guid,data.id );
1284 continue;
1287 if (data.phaseMask==0)
1289 sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id );
1290 data.phaseMask = 1;
1293 if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system
1294 AddGameobjectToGrid(guid, &data);
1295 ++count;
1297 } while (result->NextRow());
1299 delete result;
1301 sLog.outString();
1302 sLog.outString( ">> Loaded %lu gameobjects", (unsigned long)mGameObjectDataMap.size());
1305 void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
1307 uint8 mask = data->spawnMask;
1308 for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1310 if(mask & 1)
1312 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1313 uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1315 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1316 cell_guids.gameobjects.insert(guid);
1321 void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
1323 uint8 mask = data->spawnMask;
1324 for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1326 if(mask & 1)
1328 CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY);
1329 uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1331 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1332 cell_guids.gameobjects.erase(guid);
1337 void ObjectMgr::LoadCreatureRespawnTimes()
1339 // remove outdated data
1340 WorldDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
1342 uint32 count = 0;
1344 QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
1346 if(!result)
1348 barGoLink bar(1);
1350 bar.step();
1352 sLog.outString();
1353 sLog.outString(">> Loaded 0 creature respawn time.");
1354 return;
1357 barGoLink bar(result->GetRowCount());
1361 Field *fields = result->Fetch();
1362 bar.step();
1364 uint32 loguid = fields[0].GetUInt32();
1365 uint64 respawn_time = fields[1].GetUInt64();
1366 uint32 instance = fields[2].GetUInt32();
1368 mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
1370 ++count;
1371 } while (result->NextRow());
1373 delete result;
1375 sLog.outString( ">> Loaded %lu creature respawn times", (unsigned long)mCreatureRespawnTimes.size() );
1376 sLog.outString();
1379 void ObjectMgr::LoadGameobjectRespawnTimes()
1381 // remove outdated data
1382 WorldDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
1384 uint32 count = 0;
1386 QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM gameobject_respawn");
1388 if(!result)
1390 barGoLink bar(1);
1392 bar.step();
1394 sLog.outString();
1395 sLog.outString(">> Loaded 0 gameobject respawn time.");
1396 return;
1399 barGoLink bar(result->GetRowCount());
1403 Field *fields = result->Fetch();
1404 bar.step();
1406 uint32 loguid = fields[0].GetUInt32();
1407 uint64 respawn_time = fields[1].GetUInt64();
1408 uint32 instance = fields[2].GetUInt32();
1410 mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
1412 ++count;
1413 } while (result->NextRow());
1415 delete result;
1417 sLog.outString( ">> Loaded %lu gameobject respawn times", (unsigned long)mGORespawnTimes.size() );
1418 sLog.outString();
1421 // name must be checked to correctness (if received) before call this function
1422 uint64 ObjectMgr::GetPlayerGUIDByName(std::string name) const
1424 uint64 guid = 0;
1426 CharacterDatabase.escape_string(name);
1428 // Player name safe to sending to DB (checked at login) and this function using
1429 QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
1430 if(result)
1432 guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
1434 delete result;
1437 return guid;
1440 bool ObjectMgr::GetPlayerNameByGUID(const uint64 &guid, std::string &name) const
1442 // prevent DB access for online player
1443 if(Player* player = GetPlayer(guid))
1445 name = player->GetName();
1446 return true;
1449 QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1451 if(result)
1453 name = (*result)[0].GetCppString();
1454 delete result;
1455 return true;
1458 return false;
1461 uint32 ObjectMgr::GetPlayerTeamByGUID(const uint64 &guid) const
1463 QueryResult *result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1465 if(result)
1467 uint8 race = (*result)[0].GetUInt8();
1468 delete result;
1469 return Player::TeamForRace(race);
1472 return 0;
1475 uint32 ObjectMgr::GetPlayerAccountIdByGUID(const uint64 &guid) const
1477 QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1478 if(result)
1480 uint32 acc = (*result)[0].GetUInt32();
1481 delete result;
1482 return acc;
1485 return 0;
1488 uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
1490 QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", name.c_str());
1491 if(result)
1493 uint32 acc = (*result)[0].GetUInt32();
1494 delete result;
1495 return acc;
1498 return 0;
1501 void ObjectMgr::LoadItemLocales()
1503 mItemLocaleMap.clear(); // need for reload case
1505 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");
1507 if(!result)
1509 barGoLink bar(1);
1511 bar.step();
1513 sLog.outString();
1514 sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
1515 return;
1518 barGoLink bar(result->GetRowCount());
1522 Field *fields = result->Fetch();
1523 bar.step();
1525 uint32 entry = fields[0].GetUInt32();
1527 ItemLocale& data = mItemLocaleMap[entry];
1529 for(int i = 1; i < MAX_LOCALE; ++i)
1531 std::string str = fields[1+2*(i-1)].GetCppString();
1532 if(!str.empty())
1534 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1535 if(idx >= 0)
1537 if(data.Name.size() <= idx)
1538 data.Name.resize(idx+1);
1540 data.Name[idx] = str;
1544 str = fields[1+2*(i-1)+1].GetCppString();
1545 if(!str.empty())
1547 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1548 if(idx >= 0)
1550 if(data.Description.size() <= idx)
1551 data.Description.resize(idx+1);
1553 data.Description[idx] = str;
1557 } while (result->NextRow());
1559 delete result;
1561 sLog.outString();
1562 sLog.outString( ">> Loaded %lu Item locale strings", (unsigned long)mItemLocaleMap.size() );
1565 struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader>
1567 template<class D>
1568 void convert_from_str(uint32 /*field_pos*/, char *src, D &dst)
1570 dst = D(objmgr.GetScriptId(src));
1574 void ObjectMgr::LoadItemPrototypes()
1576 SQLItemLoader loader;
1577 loader.Load(sItemStorage);
1578 sLog.outString( ">> Loaded %u item prototypes", sItemStorage.RecordCount );
1579 sLog.outString();
1581 // check data correctness
1582 for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
1584 ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
1585 ItemEntry const *dbcitem = sItemStore.LookupEntry(i);
1586 if(!proto)
1588 /* to many errors, and possible not all items really used in game
1589 if (dbcitem)
1590 sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
1592 continue;
1595 if(dbcitem)
1597 if(proto->Class != dbcitem->Class)
1599 sLog.outErrorDb("Item (Entry: %u) not correct class %u, must be %u (still using DB value).",i,proto->Class,dbcitem->Class);
1600 // It safe let use Class from DB
1602 /* disabled: have some strange wrong cases for Subclass values.
1603 for enable also uncomment Subclass field in ItemEntry structure and in Itemfmt[]
1604 if(proto->SubClass != dbcitem->SubClass)
1606 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);
1607 // It safe let use Subclass from DB
1611 if(proto->Unk0 != dbcitem->Unk0)
1613 sLog.outErrorDb("Item (Entry: %u) not correct %i Unk0, must be %i (still using DB value).",i,proto->Unk0,dbcitem->Unk0);
1614 // It safe let use Unk0 from DB
1617 if(proto->Material != dbcitem->Material)
1619 sLog.outErrorDb("Item (Entry: %u) not correct %i material, must be %i (still using DB value).",i,proto->Material,dbcitem->Material);
1620 // It safe let use Material from DB
1623 if(proto->InventoryType != dbcitem->InventoryType)
1625 sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType);
1626 // It safe let use InventoryType from DB
1629 if(proto->DisplayInfoID != dbcitem->DisplayId)
1631 sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i,proto->DisplayInfoID,dbcitem->DisplayId);
1632 const_cast<ItemPrototype*>(proto)->DisplayInfoID = dbcitem->DisplayId;
1634 if(proto->Sheath != dbcitem->Sheath)
1636 sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u (using it).",i,proto->Sheath,dbcitem->Sheath);
1637 const_cast<ItemPrototype*>(proto)->Sheath = dbcitem->Sheath;
1640 else
1642 sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existed items).",i);
1645 if(proto->Class >= MAX_ITEM_CLASS)
1647 sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class);
1648 const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_MISC;
1651 if(proto->SubClass >= MaxItemSubclassValues[proto->Class])
1653 sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u",i,proto->SubClass,proto->Class);
1654 const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
1657 if(proto->Quality >= MAX_ITEM_QUALITY)
1659 sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)",i,proto->Quality);
1660 const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
1663 if(proto->BuyCount <= 0)
1665 sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).",i,proto->BuyCount);
1666 const_cast<ItemPrototype*>(proto)->BuyCount = 1;
1669 if(proto->InventoryType >= MAX_INVTYPE)
1671 sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType);
1672 const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
1675 if(proto->RequiredSkill >= MAX_SKILL_TYPE)
1677 sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
1678 const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
1682 // can be used in equip slot, as page read use in inventory, or spell casting at use
1683 bool req = proto->InventoryType!=INVTYPE_NON_EQUIP || proto->PageText;
1684 if(!req)
1686 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
1688 if(proto->Spells[j].SpellId)
1690 req = true;
1691 break;
1696 if(req)
1698 if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
1699 sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.",i,proto->AllowableClass);
1701 if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
1702 sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.",i,proto->AllowableRace);
1706 if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
1708 sLog.outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)",i,proto->RequiredSpell);
1709 const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
1712 if(proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
1713 sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.",i,proto->RequiredReputationRank);
1715 if(proto->RequiredReputationFaction)
1717 if(!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
1719 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)",i,proto->RequiredReputationFaction);
1720 const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
1723 if(proto->RequiredReputationRank == MIN_REPUTATION_RANK)
1724 sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.",i);
1726 else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK)
1727 sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i);
1729 if(proto->MaxCount < -1)
1731 sLog.outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.",i,proto->MaxCount);
1732 const_cast<ItemPrototype*>(proto)->MaxCount = -1;
1735 if(proto->Stackable == 0)
1737 sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.",i,proto->Stackable);
1738 const_cast<ItemPrototype*>(proto)->Stackable = 1;
1740 else if(proto->Stackable < -1)
1742 sLog.outErrorDb("Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.",i,proto->Stackable);
1743 const_cast<ItemPrototype*>(proto)->Stackable = -1;
1745 else if(proto->Stackable > 1000)
1747 sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (1000).",i,proto->Stackable);
1748 const_cast<ItemPrototype*>(proto)->Stackable = 1000;
1751 if(proto->ContainerSlots > MAX_BAG_SIZE)
1753 sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).",i,proto->ContainerSlots,MAX_BAG_SIZE);
1754 const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE;
1757 if(proto->StatsCount > MAX_ITEM_PROTO_STATS)
1759 sLog.outErrorDb("Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).",i,proto->StatsCount,MAX_ITEM_PROTO_STATS);
1760 const_cast<ItemPrototype*>(proto)->StatsCount = MAX_ITEM_PROTO_STATS;
1763 for (int j = 0; j < MAX_ITEM_PROTO_STATS; ++j)
1765 // for ItemStatValue != 0
1766 if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
1768 sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
1769 const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
1772 switch(proto->ItemStat[j].ItemStatType)
1774 case ITEM_MOD_SPELL_HEALING_DONE:
1775 case ITEM_MOD_SPELL_DAMAGE_DONE:
1776 sLog.outErrorDb("Item (Entry: %u) has deprecated stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
1777 break;
1778 default:
1779 break;
1783 for (int j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j)
1785 if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
1787 sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)",i,j+1,proto->Damage[j].DamageType);
1788 const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
1792 // special format
1793 if((proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN_PET))
1795 // spell_1
1796 if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
1798 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);
1799 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1800 const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1801 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1802 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1805 // spell_2 have learning spell
1806 if(proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
1808 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);
1809 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1810 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1811 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1813 else if(!proto->Spells[1].SpellId)
1815 sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.",i,1+1);
1816 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1817 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1819 else
1821 SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
1822 if(!spellInfo)
1824 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
1825 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1826 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1827 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1829 // allowed only in special format
1830 else if((proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN) || (proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN_PET))
1832 sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
1833 const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1834 const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1835 const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1839 // spell_3*,spell_4*,spell_5* is empty
1840 for (int j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j)
1842 if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
1844 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
1845 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1846 const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1848 else if(proto->Spells[j].SpellId != 0)
1850 sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format",i,j+1,proto->Spells[j].SpellId);
1851 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1855 // normal spell list
1856 else
1858 for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
1860 if (proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
1862 sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
1863 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1864 const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1866 // on hit can be sued only at weapon
1867 else if (proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_CHANCE_ON_HIT)
1869 if(proto->Class != ITEM_CLASS_WEAPON)
1870 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);
1873 if(proto->Spells[j].SpellId)
1875 SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
1876 if(!spellInfo)
1878 sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
1879 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1881 // allowed only in special format
1882 else if((proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN) || (proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN_PET))
1884 sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
1885 const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1891 if(proto->Bonding >= MAX_BIND_TYPE)
1892 sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)",i,proto->Bonding);
1894 if(proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
1895 sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i,proto->PageText);
1897 if(proto->LockID && !sLockStore.LookupEntry(proto->LockID))
1898 sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)",i,proto->LockID);
1900 if(proto->Sheath >= MAX_SHEATHETYPE)
1902 sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)",i,proto->Sheath);
1903 const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
1906 if(proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
1908 sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)",i,proto->RandomProperty);
1909 const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
1912 if(proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
1914 sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)",i,proto->RandomSuffix);
1915 const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
1918 if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
1920 sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet);
1921 const_cast<ItemPrototype*>(proto)->ItemSet = 0;
1924 if(proto->Area && !GetAreaEntryByAreaID(proto->Area))
1925 sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)",i,proto->Area);
1927 if(proto->Map && !sMapStore.LookupEntry(proto->Map))
1928 sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map);
1930 if(proto->BagFamily)
1932 // check bits
1933 for(uint32 j = 0; j < sizeof(proto->BagFamily)*8; ++j)
1935 uint32 mask = 1 << j;
1936 if((proto->BagFamily & mask)==0)
1937 continue;
1939 ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1);
1940 if(!bf)
1942 sLog.outErrorDb("Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit",i);
1943 const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
1944 continue;
1947 if(BAG_FAMILY_MASK_CURRENCY_TOKENS & mask)
1949 CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(proto->ItemId);
1950 if(!ctEntry)
1952 sLog.outErrorDb("Item (Entry: %u) has currency bag family bit set in BagFamily but not listed in CurrencyTypes.dbc, remove bit",i);
1953 const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
1959 if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
1960 sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory);
1962 for (int j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
1964 if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
1966 sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)",i,j+1,proto->Socket[j].Color);
1967 const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
1971 if(proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
1972 sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)",i,proto->GemProperties);
1974 if(proto->FoodType >= MAX_PET_DIET)
1976 sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType);
1977 const_cast<ItemPrototype*>(proto)->FoodType = 0;
1980 if(proto->ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(proto->ItemLimitCategory))
1982 sLog.outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)",i,proto->ItemLimitCategory);
1983 const_cast<ItemPrototype*>(proto)->ItemLimitCategory = 0;
1986 if(proto->HolidayId && !sHolidaysStore.LookupEntry(proto->HolidayId))
1988 sLog.outErrorDb("Item (Entry: %u) has wrong HolidayId value (%u)", i, proto->HolidayId);
1989 const_cast<ItemPrototype*>(proto)->HolidayId = 0;
1994 void ObjectMgr::LoadItemRequiredTarget()
1996 m_ItemRequiredTarget.clear(); // needed for reload case
1998 uint32 count = 0;
2000 QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM item_required_target");
2002 if (!result)
2004 barGoLink bar(1);
2006 bar.step();
2008 sLog.outString();
2009 sLog.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty.");
2010 return;
2013 barGoLink bar(result->GetRowCount());
2017 Field *fields = result->Fetch();
2018 bar.step();
2020 uint32 uiItemId = fields[0].GetUInt32();
2021 uint32 uiType = fields[1].GetUInt32();
2022 uint32 uiTargetEntry = fields[2].GetUInt32();
2024 ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(uiItemId);
2026 if (!pItemProto)
2028 sLog.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.",uiItemId,uiTargetEntry);
2029 continue;
2032 bool bIsItemSpellValid = false;
2034 for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
2036 if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId))
2038 if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE ||
2039 pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)
2041 SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(pSpellInfo->Id);
2042 if (bounds.first != bounds.second)
2043 break;
2045 for (int j = 0; j < 3; ++j)
2047 if (pSpellInfo->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE ||
2048 pSpellInfo->EffectImplicitTargetB[j] == TARGET_CHAIN_DAMAGE ||
2049 pSpellInfo->EffectImplicitTargetA[j] == TARGET_DUELVSPLAYER ||
2050 pSpellInfo->EffectImplicitTargetB[j] == TARGET_DUELVSPLAYER)
2052 bIsItemSpellValid = true;
2053 break;
2056 if (bIsItemSpellValid)
2057 break;
2062 if (!bIsItemSpellValid)
2064 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);
2065 continue;
2068 if (!uiType || uiType > MAX_ITEM_REQ_TARGET_TYPE)
2070 sLog.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.",uiType,uiTargetEntry);
2071 continue;
2074 if (!uiTargetEntry)
2076 sLog.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).",uiType);
2077 continue;
2080 if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiTargetEntry))
2082 sLog.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.",uiTargetEntry);
2083 continue;
2086 m_ItemRequiredTarget.insert(ItemRequiredTargetMap::value_type(uiItemId,ItemRequiredTarget(ItemRequiredTargetType(uiType),uiTargetEntry)));
2088 ++count;
2089 } while (result->NextRow());
2091 delete result;
2093 sLog.outString();
2094 sLog.outString(">> Loaded %u Item required targets", count);
2097 void ObjectMgr::LoadPetLevelInfo()
2099 // Loading levels data
2101 // 0 1 2 3 4 5 6 7 8 9
2102 QueryResult *result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
2104 uint32 count = 0;
2106 if (!result)
2108 barGoLink bar( 1 );
2110 sLog.outString();
2111 sLog.outString( ">> Loaded %u level pet stats definitions", count );
2112 sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table.");
2113 return;
2116 barGoLink bar( result->GetRowCount() );
2120 Field* fields = result->Fetch();
2122 uint32 creature_id = fields[0].GetUInt32();
2123 if(!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
2125 sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.",creature_id);
2126 continue;
2129 uint32 current_level = fields[1].GetUInt32();
2130 if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2132 if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2133 sLog.outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level);
2134 else
2136 sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `pet_levelstats` table, ignoring.",current_level);
2137 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2139 continue;
2141 else if(current_level < 1)
2143 sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.",current_level);
2144 continue;
2147 PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
2149 if(pInfoMapEntry==NULL)
2150 pInfoMapEntry = new PetLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2152 // data for level 1 stored in [0] array element, ...
2153 PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level-1];
2155 pLevelInfo->health = fields[2].GetUInt16();
2156 pLevelInfo->mana = fields[3].GetUInt16();
2157 pLevelInfo->armor = fields[9].GetUInt16();
2159 for (int i = 0; i < MAX_STATS; i++)
2161 pLevelInfo->stats[i] = fields[i+4].GetUInt16();
2164 bar.step();
2165 ++count;
2167 while (result->NextRow());
2169 delete result;
2171 sLog.outString();
2172 sLog.outString( ">> Loaded %u level pet stats definitions", count );
2175 // Fill gaps and check integrity
2176 for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
2178 PetLevelInfo* pInfo = itr->second;
2180 // fatal error if no level 1 data
2181 if(!pInfo || pInfo[0].health == 0 )
2183 sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!",itr->first);
2184 exit(1);
2187 // fill level gaps
2188 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2190 if(pInfo[level].health == 0)
2192 sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.",itr->first,level+1, level);
2193 pInfo[level] = pInfo[level-1];
2199 PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
2201 if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2202 level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
2204 PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
2205 if(itr == petInfo.end())
2206 return NULL;
2208 return &itr->second[level-1]; // data for level 1 stored in [0] array element, ...
2211 void ObjectMgr::LoadPlayerInfo()
2213 // Load playercreate
2215 // 0 1 2 3 4 5 6
2216 QueryResult *result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z FROM playercreateinfo");
2218 uint32 count = 0;
2220 if (!result)
2222 barGoLink bar( 1 );
2224 sLog.outString();
2225 sLog.outString( ">> Loaded %u player create definitions", count );
2226 sLog.outErrorDb( "Error loading `playercreateinfo` table or empty table.");
2227 exit(1);
2230 barGoLink bar( result->GetRowCount() );
2234 Field* fields = result->Fetch();
2236 uint32 current_race = fields[0].GetUInt32();
2237 uint32 current_class = fields[1].GetUInt32();
2238 uint32 mapId = fields[2].GetUInt32();
2239 uint32 zoneId = fields[3].GetUInt32();
2240 float positionX = fields[4].GetFloat();
2241 float positionY = fields[5].GetFloat();
2242 float positionZ = fields[6].GetFloat();
2244 if(current_race >= MAX_RACES)
2246 sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
2247 continue;
2250 ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
2251 if(!rEntry)
2253 sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
2254 continue;
2257 if(current_class >= MAX_CLASSES)
2259 sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
2260 continue;
2263 if(!sChrClassesStore.LookupEntry(current_class))
2265 sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
2266 continue;
2269 // accept DB data only for valid position (and non instanceable)
2270 if( !MapManager::IsValidMapCoord(mapId,positionX,positionY,positionZ) )
2272 sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
2273 continue;
2276 if( sMapStore.LookupEntry(mapId)->Instanceable() )
2278 sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
2279 continue;
2282 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2284 pInfo->mapId = mapId;
2285 pInfo->zoneId = zoneId;
2286 pInfo->positionX = positionX;
2287 pInfo->positionY = positionY;
2288 pInfo->positionZ = positionZ;
2290 pInfo->displayId_m = rEntry->model_m;
2291 pInfo->displayId_f = rEntry->model_f;
2293 bar.step();
2294 ++count;
2296 while (result->NextRow());
2298 delete result;
2300 sLog.outString();
2301 sLog.outString( ">> Loaded %u player create definitions", count );
2304 // Load playercreate items
2306 // 0 1 2 3
2307 QueryResult *result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
2309 uint32 count = 0;
2311 if (!result)
2313 barGoLink bar( 1 );
2315 bar.step();
2317 sLog.outString();
2318 sLog.outString( ">> Loaded %u custom player create items", count );
2320 else
2322 barGoLink bar( result->GetRowCount() );
2326 Field* fields = result->Fetch();
2328 uint32 current_race = fields[0].GetUInt32();
2329 if(current_race >= MAX_RACES)
2331 sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.",current_race);
2332 continue;
2335 uint32 current_class = fields[1].GetUInt32();
2336 if(current_class >= MAX_CLASSES)
2338 sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.",current_class);
2339 continue;
2342 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2344 uint32 item_id = fields[2].GetUInt32();
2346 if(!GetItemPrototype(item_id))
2348 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);
2349 continue;
2352 uint32 amount = fields[3].GetUInt32();
2354 if(!amount)
2356 sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.",item_id,current_race,current_class);
2357 continue;
2360 pInfo->item.push_back(PlayerCreateInfoItem( item_id, amount));
2362 bar.step();
2363 ++count;
2365 while(result->NextRow());
2367 delete result;
2369 sLog.outString();
2370 sLog.outString( ">> Loaded %u custom player create items", count );
2374 // Load playercreate spells
2376 // 0 1 2
2377 QueryResult *result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell");
2379 uint32 count = 0;
2381 if (!result)
2383 barGoLink bar( 1 );
2385 sLog.outString();
2386 sLog.outString( ">> Loaded %u player create spells", count );
2387 sLog.outErrorDb( "Error loading `playercreateinfo_spell` table or empty table.");
2389 else
2391 barGoLink bar( result->GetRowCount() );
2395 Field* fields = result->Fetch();
2397 uint32 current_race = fields[0].GetUInt32();
2398 if(current_race >= MAX_RACES)
2400 sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.",current_race);
2401 continue;
2404 uint32 current_class = fields[1].GetUInt32();
2405 if(current_class >= MAX_CLASSES)
2407 sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.",current_class);
2408 continue;
2411 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2412 pInfo->spell.push_back(fields[2].GetUInt32());
2414 bar.step();
2415 ++count;
2417 while( result->NextRow() );
2419 delete result;
2421 sLog.outString();
2422 sLog.outString( ">> Loaded %u player create spells", count );
2426 // Load playercreate actions
2428 // 0 1 2 3 4
2429 QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");
2431 uint32 count = 0;
2433 if (!result)
2435 barGoLink bar( 1 );
2437 sLog.outString();
2438 sLog.outString( ">> Loaded %u player create actions", count );
2439 sLog.outErrorDb( "Error loading `playercreateinfo_action` table or empty table.");
2441 else
2443 barGoLink bar( result->GetRowCount() );
2447 Field* fields = result->Fetch();
2449 uint32 current_race = fields[0].GetUInt32();
2450 if(current_race >= MAX_RACES)
2452 sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.",current_race);
2453 continue;
2456 uint32 current_class = fields[1].GetUInt32();
2457 if(current_class >= MAX_CLASSES)
2459 sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.",current_class);
2460 continue;
2463 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2464 pInfo->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt8(),fields[3].GetUInt32(),fields[4].GetUInt8()));
2466 bar.step();
2467 ++count;
2469 while( result->NextRow() );
2471 delete result;
2473 sLog.outString();
2474 sLog.outString( ">> Loaded %u player create actions", count );
2478 // Loading levels data (class only dependent)
2480 // 0 1 2 3
2481 QueryResult *result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
2483 uint32 count = 0;
2485 if (!result)
2487 barGoLink bar( 1 );
2489 sLog.outString();
2490 sLog.outString( ">> Loaded %u level health/mana definitions", count );
2491 sLog.outErrorDb( "Error loading `player_classlevelstats` table or empty table.");
2492 exit(1);
2495 barGoLink bar( result->GetRowCount() );
2499 Field* fields = result->Fetch();
2501 uint32 current_class = fields[0].GetUInt32();
2502 if(current_class >= MAX_CLASSES)
2504 sLog.outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.",current_class);
2505 continue;
2508 uint32 current_level = fields[1].GetUInt32();
2509 if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2511 if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2512 sLog.outErrorDb("Wrong (> %u) level %u in `player_classlevelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level);
2513 else
2515 sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_classlevelstats` table, ignoring.",current_level);
2516 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2518 continue;
2521 PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
2523 if(!pClassInfo->levelInfo)
2524 pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2526 PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level-1];
2528 pClassLevelInfo->basehealth = fields[2].GetUInt16();
2529 pClassLevelInfo->basemana = fields[3].GetUInt16();
2531 bar.step();
2532 ++count;
2534 while (result->NextRow());
2536 delete result;
2538 sLog.outString();
2539 sLog.outString( ">> Loaded %u level health/mana definitions", count );
2542 // Fill gaps and check integrity
2543 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2545 // skip non existed classes
2546 if(!sChrClassesStore.LookupEntry(class_))
2547 continue;
2549 PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
2551 // fatal error if no level 1 data
2552 if(!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0 )
2554 sLog.outErrorDb("Class %i Level 1 does not have health/mana data!",class_);
2555 exit(1);
2558 // fill level gaps
2559 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2561 if(pClassInfo->levelInfo[level].basehealth == 0)
2563 sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.",class_,level+1, level);
2564 pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level-1];
2569 // Loading levels data (class/race dependent)
2571 // 0 1 2 3 4 5 6 7
2572 QueryResult *result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
2574 uint32 count = 0;
2576 if (!result)
2578 barGoLink bar( 1 );
2580 sLog.outString();
2581 sLog.outString( ">> Loaded %u level stats definitions", count );
2582 sLog.outErrorDb( "Error loading `player_levelstats` table or empty table.");
2583 exit(1);
2586 barGoLink bar( result->GetRowCount() );
2590 Field* fields = result->Fetch();
2592 uint32 current_race = fields[0].GetUInt32();
2593 if(current_race >= MAX_RACES)
2595 sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.",current_race);
2596 continue;
2599 uint32 current_class = fields[1].GetUInt32();
2600 if(current_class >= MAX_CLASSES)
2602 sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.",current_class);
2603 continue;
2606 uint32 current_level = fields[2].GetUInt32();
2607 if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2609 if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2610 sLog.outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.",STRONG_MAX_LEVEL,current_level);
2611 else
2613 sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_levelstats` table, ignoring.",current_level);
2614 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2616 continue;
2619 PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2621 if(!pInfo->levelInfo)
2622 pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2624 PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level-1];
2626 for (int i = 0; i < MAX_STATS; i++)
2628 pLevelInfo->stats[i] = fields[i+3].GetUInt8();
2631 bar.step();
2632 ++count;
2634 while (result->NextRow());
2636 delete result;
2638 sLog.outString();
2639 sLog.outString( ">> Loaded %u level stats definitions", count );
2642 // Fill gaps and check integrity
2643 for (int race = 0; race < MAX_RACES; ++race)
2645 // skip non existed races
2646 if(!sChrRacesStore.LookupEntry(race))
2647 continue;
2649 for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2651 // skip non existed classes
2652 if(!sChrClassesStore.LookupEntry(class_))
2653 continue;
2655 PlayerInfo* pInfo = &playerInfo[race][class_];
2657 // skip non loaded combinations
2658 if(!pInfo->displayId_m || !pInfo->displayId_f)
2659 continue;
2661 // skip expansion races if not playing with expansion
2662 if (sWorld.getConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
2663 continue;
2665 // skip expansion classes if not playing with expansion
2666 if (sWorld.getConfig(CONFIG_EXPANSION) < 2 && class_ == CLASS_DEATH_KNIGHT)
2667 continue;
2669 // fatal error if no level 1 data
2670 if(!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0 )
2672 sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!",race,class_);
2673 exit(1);
2676 // fill level gaps
2677 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2679 if(pInfo->levelInfo[level].stats[0] == 0)
2681 sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.",race,class_,level+1, level);
2682 pInfo->levelInfo[level] = pInfo->levelInfo[level-1];
2688 // Loading xp per level data
2690 mPlayerXPperLevel.resize(sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
2691 for (uint32 level = 0; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2692 mPlayerXPperLevel[level] = 0;
2694 // 0 1
2695 QueryResult *result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
2697 uint32 count = 0;
2699 if (!result)
2701 barGoLink bar( 1 );
2703 sLog.outString();
2704 sLog.outString( ">> Loaded %u xp for level definitions", count );
2705 sLog.outErrorDb( "Error loading `player_xp_for_level` table or empty table.");
2706 exit(1);
2709 barGoLink bar( result->GetRowCount() );
2713 Field* fields = result->Fetch();
2715 uint32 current_level = fields[0].GetUInt32();
2716 uint32 current_xp = fields[1].GetUInt32();
2718 if(current_level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2720 if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
2721 sLog.outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL,current_level);
2722 else
2724 sLog.outDetail("Unused (> MaxPlayerLevel in mangosd.conf) level %u in `player_xp_for_levels` table, ignoring.",current_level);
2725 ++count; // make result loading percent "expected" correct in case disabled detail mode for example.
2727 continue;
2729 //PlayerXPperLevel
2730 mPlayerXPperLevel[current_level] = current_xp;
2731 bar.step();
2732 ++count;
2734 while (result->NextRow());
2736 delete result;
2738 sLog.outString();
2739 sLog.outString( ">> Loaded %u xp for level definitions", count );
2742 // fill level gaps
2743 for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2745 if( mPlayerXPperLevel[level] == 0)
2747 sLog.outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.",level+1, level);
2748 mPlayerXPperLevel[level] = mPlayerXPperLevel[level-1]+100;
2753 void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const
2755 if(level < 1 || class_ >= MAX_CLASSES)
2756 return;
2758 PlayerClassInfo const* pInfo = &playerClassInfo[class_];
2760 if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2761 level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
2763 *info = pInfo->levelInfo[level-1];
2766 void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
2768 if(level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
2769 return;
2771 PlayerInfo const* pInfo = &playerInfo[race][class_];
2772 if(pInfo->displayId_m==0 || pInfo->displayId_f==0)
2773 return;
2775 if(level <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2776 *info = pInfo->levelInfo[level-1];
2777 else
2778 BuildPlayerLevelInfo(race,class_,level,info);
2781 void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
2783 // base data (last known level)
2784 *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1];
2786 for(int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
2788 switch(_class)
2790 case CLASS_WARRIOR:
2791 info->stats[STAT_STRENGTH] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
2792 info->stats[STAT_STAMINA] += (lvl > 23 ? 2: (lvl > 1 ? 1: 0));
2793 info->stats[STAT_AGILITY] += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
2794 info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
2795 info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl%2) ? 1: 0);
2796 break;
2797 case CLASS_PALADIN:
2798 info->stats[STAT_STRENGTH] += (lvl > 3 ? 1: 0);
2799 info->stats[STAT_STAMINA] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
2800 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
2801 info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
2802 info->stats[STAT_SPIRIT] += (lvl > 7 ? 1: 0);
2803 break;
2804 case CLASS_HUNTER:
2805 info->stats[STAT_STRENGTH] += (lvl > 4 ? 1: 0);
2806 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
2807 info->stats[STAT_AGILITY] += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
2808 info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
2809 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
2810 break;
2811 case CLASS_ROGUE:
2812 info->stats[STAT_STRENGTH] += (lvl > 5 ? 1: 0);
2813 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
2814 info->stats[STAT_AGILITY] += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
2815 info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
2816 info->stats[STAT_SPIRIT] += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
2817 break;
2818 case CLASS_PRIEST:
2819 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
2820 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
2821 info->stats[STAT_AGILITY] += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
2822 info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
2823 info->stats[STAT_SPIRIT] += (lvl > 3 ? 1: 0);
2824 break;
2825 case CLASS_SHAMAN:
2826 info->stats[STAT_STRENGTH] += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
2827 info->stats[STAT_STAMINA] += (lvl > 4 ? 1: 0);
2828 info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl%2) ? 1: 0);
2829 info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
2830 info->stats[STAT_SPIRIT] += (lvl > 4 ? 1: 0);
2831 break;
2832 case CLASS_MAGE:
2833 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
2834 info->stats[STAT_STAMINA] += (lvl > 5 ? 1: 0);
2835 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
2836 info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
2837 info->stats[STAT_SPIRIT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
2838 break;
2839 case CLASS_WARLOCK:
2840 info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl%2) ? 1: 0);
2841 info->stats[STAT_STAMINA] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
2842 info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl%2) ? 1: 0);
2843 info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
2844 info->stats[STAT_SPIRIT] += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
2845 break;
2846 case CLASS_DRUID:
2847 info->stats[STAT_STRENGTH] += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
2848 info->stats[STAT_STAMINA] += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
2849 info->stats[STAT_AGILITY] += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
2850 info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
2851 info->stats[STAT_SPIRIT] += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
2856 void ObjectMgr::LoadGuilds()
2858 Guild *newguild;
2859 uint32 count = 0;
2861 QueryResult *result = CharacterDatabase.Query( "SELECT guildid FROM guild" );
2863 if( !result )
2866 barGoLink bar( 1 );
2868 bar.step();
2870 sLog.outString();
2871 sLog.outString( ">> Loaded %u guild definitions", count );
2872 return;
2875 barGoLink bar( result->GetRowCount() );
2879 Field *fields = result->Fetch();
2881 bar.step();
2882 ++count;
2884 newguild = new Guild;
2885 if(!newguild->LoadGuildFromDB(fields[0].GetUInt32()))
2887 newguild->Disband();
2888 delete newguild;
2889 continue;
2891 AddGuild(newguild);
2893 }while( result->NextRow() );
2895 delete result;
2897 //delete unused LogGuid records in guild_eventlog and guild_bank_eventlog table
2898 //you can comment these lines if you don't plan to change CONFIG_GUILD_EVENT_LOG_COUNT and CONFIG_GUILD_BANK_EVENT_LOG_COUNT
2899 CharacterDatabase.PQuery("DELETE FROM guild_eventlog WHERE LogGuid > '%u'", sWorld.getConfig(CONFIG_GUILD_EVENT_LOG_COUNT));
2900 CharacterDatabase.PQuery("DELETE FROM guild_bank_eventlog WHERE LogGuid > '%u'", sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT));
2902 sLog.outString();
2903 sLog.outString( ">> Loaded %u guild definitions", count );
2906 void ObjectMgr::LoadArenaTeams()
2908 uint32 count = 0;
2910 QueryResult *result = CharacterDatabase.Query( "SELECT arenateamid FROM arena_team" );
2912 if( !result )
2915 barGoLink bar( 1 );
2917 bar.step();
2919 sLog.outString();
2920 sLog.outString( ">> Loaded %u arenateam definitions", count );
2921 return;
2924 barGoLink bar( result->GetRowCount() );
2928 Field *fields = result->Fetch();
2930 bar.step();
2931 ++count;
2933 ArenaTeam *newarenateam = new ArenaTeam;
2934 if(!newarenateam->LoadArenaTeamFromDB(fields[0].GetUInt32()))
2936 delete newarenateam;
2937 continue;
2939 AddArenaTeam(newarenateam);
2940 }while( result->NextRow() );
2942 delete result;
2944 sLog.outString();
2945 sLog.outString( ">> Loaded %u arenateam definitions", count );
2948 void ObjectMgr::LoadGroups()
2950 // -- loading groups --
2951 Group *group = NULL;
2952 uint64 leaderGuid = 0;
2953 uint32 count = 0;
2954 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2955 QueryResult *result = CharacterDatabase.Query("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, leaderGuid FROM groups");
2957 if( !result )
2959 barGoLink bar( 1 );
2961 bar.step();
2963 sLog.outString();
2964 sLog.outString( ">> Loaded %u group definitions", count );
2965 return;
2968 barGoLink bar( result->GetRowCount() );
2972 bar.step();
2973 Field *fields = result->Fetch();
2974 ++count;
2975 leaderGuid = MAKE_NEW_GUID(fields[15].GetUInt32(),0,HIGHGUID_PLAYER);
2977 group = new Group;
2978 if(!group->LoadGroupFromDB(leaderGuid, result, false))
2980 group->Disband();
2981 delete group;
2982 continue;
2984 AddGroup(group);
2985 }while( result->NextRow() );
2987 delete result;
2989 sLog.outString();
2990 sLog.outString( ">> Loaded %u group definitions", count );
2992 // -- loading members --
2993 count = 0;
2994 group = NULL;
2995 leaderGuid = 0;
2996 // 0 1 2 3
2997 result = CharacterDatabase.Query("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid");
2998 if(!result)
3000 barGoLink bar2( 1 );
3001 bar2.step();
3003 else
3005 barGoLink bar2( result->GetRowCount() );
3008 bar2.step();
3009 Field *fields = result->Fetch();
3010 count++;
3011 leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER);
3012 if(!group || group->GetLeaderGUID() != leaderGuid)
3014 group = GetGroupByLeader(leaderGuid);
3015 if(!group)
3017 sLog.outErrorDb("Incorrect entry in group_member table : no group with leader %d for member %d!", fields[3].GetUInt32(), fields[0].GetUInt32());
3018 CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
3019 continue;
3023 if(!group->LoadMemberFromDB(fields[0].GetUInt32(), fields[2].GetUInt8(), fields[1].GetBool()))
3025 sLog.outErrorDb("Incorrect entry in group_member table : member %d cannot be added to player %d's group!", fields[0].GetUInt32(), fields[3].GetUInt32());
3026 CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
3028 }while( result->NextRow() );
3029 delete result;
3032 // clean groups
3033 // TODO: maybe delete from the DB before loading in this case
3034 for(GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end();)
3036 if((*itr)->GetMembersCount() < 2)
3038 (*itr)->Disband();
3039 delete *itr;
3040 mGroupSet.erase(itr++);
3042 else
3043 ++itr;
3046 // -- loading instances --
3047 count = 0;
3048 group = NULL;
3049 leaderGuid = 0;
3050 result = CharacterDatabase.Query(
3051 // 0 1 2 3 4 5
3052 "SELECT leaderGuid, map, instance, permanent, difficulty, resettime, "
3053 // 6
3054 "(SELECT COUNT(*) FROM character_instance WHERE guid = leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1) "
3055 "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY leaderGuid"
3058 if(!result)
3060 barGoLink bar2( 1 );
3061 bar2.step();
3063 else
3065 barGoLink bar2( result->GetRowCount() );
3068 bar2.step();
3069 Field *fields = result->Fetch();
3070 count++;
3071 leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
3072 if(!group || group->GetLeaderGUID() != leaderGuid)
3074 group = GetGroupByLeader(leaderGuid);
3075 if(!group)
3077 sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", fields[0].GetUInt32());
3078 continue;
3082 MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt32());
3083 if(!mapEntry || !mapEntry->IsDungeon())
3085 sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt32());
3086 continue;
3089 InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapEntry->MapID, fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
3090 group->BindToInstance(save, fields[3].GetBool(), true);
3091 }while( result->NextRow() );
3092 delete result;
3095 sLog.outString();
3096 sLog.outString( ">> Loaded %u group-instance binds total", count );
3098 sLog.outString();
3099 sLog.outString( ">> Loaded %u group members total", count );
3102 void ObjectMgr::LoadQuests()
3104 // For reload case
3105 for(QuestMap::const_iterator itr=mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
3106 delete itr->second;
3107 mQuestTemplates.clear();
3109 mExclusiveQuestGroups.clear();
3111 // 0 1 2 3 4 5 6 7 8
3112 QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
3113 // 9 10 11 12 13 14 15 16
3114 "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
3115 // 17 18 19 20 21 22 23 24 25 26 27 28
3116 "QuestFlags, SpecialFlags, CharTitleId, PlayersSlain, BonusTalents, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
3117 // 29 30 31 32 33 34 35 36 37 38
3118 "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
3119 // 39 40 41 42 43 44 45 46
3120 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
3121 // 47 48 49 50 51 52 53 54
3122 "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4,"
3123 // 55 56 57 58 59 60 61 62
3124 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
3125 // 63 64 65 66
3126 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
3127 // 67 68 69 70 71 72
3128 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
3129 // 73 74 75 76 77 78
3130 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
3131 // 79 80 81 82 83 84 85 86
3132 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
3133 // 87 88 89 90 91 92 93 94 95 96
3134 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
3135 // 97 98 99 100 101 102 103 104 105 106 107
3136 "RewHonorableKills, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
3137 // 108 109 110 111 112 113 114 115
3138 "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4,"
3139 // 116 117 118 119 120 121
3140 "IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
3141 // 122 123 124 125
3142 "OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4,"
3143 // 126 127
3144 "StartScript, CompleteScript"
3145 " FROM quest_template");
3146 if(result == NULL)
3148 barGoLink bar( 1 );
3149 bar.step();
3151 sLog.outString();
3152 sLog.outString( ">> Loaded 0 quests definitions" );
3153 sLog.outErrorDb("`quest_template` table is empty!");
3154 return;
3157 // create multimap previous quest for each existed quest
3158 // some quests can have many previous maps set by NextQuestId in previous quest
3159 // for example set of race quests can lead to single not race specific quest
3160 barGoLink bar( result->GetRowCount() );
3163 bar.step();
3164 Field *fields = result->Fetch();
3166 Quest * newQuest = new Quest(fields);
3167 mQuestTemplates[newQuest->GetQuestId()] = newQuest;
3168 } while( result->NextRow() );
3170 delete result;
3172 // Post processing
3173 for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
3175 Quest * qinfo = iter->second;
3177 // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
3179 if( qinfo->GetQuestMethod() >= 3 )
3181 sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
3184 if (qinfo->QuestFlags & ~QUEST_MANGOS_FLAGS_DB_ALLOWED)
3186 sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
3187 qinfo->GetQuestId(),qinfo->QuestFlags,QUEST_MANGOS_FLAGS_DB_ALLOWED >> 16);
3188 qinfo->QuestFlags &= QUEST_MANGOS_FLAGS_DB_ALLOWED;
3191 if(qinfo->QuestFlags & QUEST_FLAGS_DAILY)
3193 if(!(qinfo->QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE))
3195 sLog.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.",qinfo->GetQuestId());
3196 qinfo->QuestFlags |= QUEST_MANGOS_FLAGS_REPEATABLE;
3200 if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED)
3202 // at auto-reward can be rewarded only RewChoiceItemId[0]
3203 for(int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
3205 if(uint32 id = qinfo->RewChoiceItemId[j])
3207 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
3208 qinfo->GetQuestId(),j+1,id,j+1);
3209 // no changes, quest ignore this data
3214 // client quest log visual (area case)
3215 if( qinfo->ZoneOrSort > 0 )
3217 if(!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
3219 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
3220 qinfo->GetQuestId(),qinfo->ZoneOrSort);
3221 // no changes, quest not dependent from this value but can have problems at client
3224 // client quest log visual (sort case)
3225 if( qinfo->ZoneOrSort < 0 )
3227 QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
3228 if( !qSort )
3230 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
3231 qinfo->GetQuestId(),qinfo->ZoneOrSort);
3232 // 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)
3234 //check SkillOrClass value (class case).
3235 if( ClassByQuestSort(-int32(qinfo->ZoneOrSort)) )
3237 // SkillOrClass should not have class case when class case already set in ZoneOrSort.
3238 if(qinfo->SkillOrClass < 0)
3240 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (class sort case) and `SkillOrClass` = %i (class case), redundant.",
3241 qinfo->GetQuestId(),qinfo->ZoneOrSort,qinfo->SkillOrClass);
3244 //check for proper SkillOrClass value (skill case)
3245 if(int32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
3247 // skill is positive value in SkillOrClass
3248 if(qinfo->SkillOrClass != skill_id )
3250 sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (skill sort case) but `SkillOrClass` does not have a corresponding value (%i).",
3251 qinfo->GetQuestId(),qinfo->ZoneOrSort,skill_id);
3252 //override, and force proper value here?
3257 // SkillOrClass (class case)
3258 if( qinfo->SkillOrClass < 0 )
3260 if( !sChrClassesStore.LookupEntry(-int32(qinfo->SkillOrClass)) )
3262 sLog.outErrorDb("Quest %u has `SkillOrClass` = %i (class case) but class (%i) does not exist",
3263 qinfo->GetQuestId(),qinfo->SkillOrClass,-qinfo->SkillOrClass);
3266 // SkillOrClass (skill case)
3267 if( qinfo->SkillOrClass > 0 )
3269 if( !sSkillLineStore.LookupEntry(qinfo->SkillOrClass) )
3271 sLog.outErrorDb("Quest %u has `SkillOrClass` = %u (skill case) but skill (%i) does not exist",
3272 qinfo->GetQuestId(),qinfo->SkillOrClass,qinfo->SkillOrClass);
3276 if( qinfo->RequiredSkillValue )
3278 if( qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue() )
3280 sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
3281 qinfo->GetQuestId(),qinfo->RequiredSkillValue,sWorld.GetConfigMaxSkillValue());
3282 // no changes, quest can't be done for this requirement
3285 if( qinfo->SkillOrClass <= 0 )
3287 sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but `SkillOrClass` = %i (class case), value ignored.",
3288 qinfo->GetQuestId(),qinfo->RequiredSkillValue,qinfo->SkillOrClass);
3289 // no changes, quest can't be done for this requirement (fail at wrong skill id)
3292 // else Skill quests can have 0 skill level, this is ok
3294 if(qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
3296 sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
3297 qinfo->GetQuestId(),qinfo->RepObjectiveFaction,qinfo->RepObjectiveFaction);
3298 // no changes, quest can't be done for this requirement
3301 if(qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
3303 sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3304 qinfo->GetQuestId(),qinfo->RequiredMinRepFaction,qinfo->RequiredMinRepFaction);
3305 // no changes, quest can't be done for this requirement
3308 if(qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
3310 sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
3311 qinfo->GetQuestId(),qinfo->RequiredMaxRepFaction,qinfo->RequiredMaxRepFaction);
3312 // no changes, quest can't be done for this requirement
3315 if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap)
3317 sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
3318 qinfo->GetQuestId(),qinfo->RequiredMinRepValue,ReputationMgr::Reputation_Cap);
3319 // no changes, quest can't be done for this requirement
3322 if(qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
3324 sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
3325 qinfo->GetQuestId(),qinfo->RequiredMaxRepValue,qinfo->RequiredMinRepValue);
3326 // no changes, quest can't be done for this requirement
3329 if(!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0 )
3331 sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
3332 qinfo->GetQuestId(),qinfo->RepObjectiveValue);
3333 // warning
3336 if(!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0 )
3338 sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
3339 qinfo->GetQuestId(),qinfo->RequiredMinRepValue);
3340 // warning
3343 if(!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0 )
3345 sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
3346 qinfo->GetQuestId(),qinfo->RequiredMaxRepValue);
3347 // warning
3350 if(qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
3352 sLog.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
3353 qinfo->GetQuestId(),qinfo->GetCharTitleId(),qinfo->GetCharTitleId());
3354 qinfo->CharTitleId = 0;
3355 // quest can't reward this title
3358 if(qinfo->SrcItemId)
3360 if(!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
3362 sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
3363 qinfo->GetQuestId(),qinfo->SrcItemId,qinfo->SrcItemId);
3364 qinfo->SrcItemId = 0; // quest can't be done for this requirement
3366 else if(qinfo->SrcItemCount==0)
3368 sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
3369 qinfo->GetQuestId(),qinfo->SrcItemId);
3370 qinfo->SrcItemCount = 1; // update to 1 for allow quest work for backward compatibility with DB
3373 else if(qinfo->SrcItemCount>0)
3375 sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
3376 qinfo->GetQuestId(),qinfo->SrcItemCount);
3377 qinfo->SrcItemCount=0; // no quest work changes in fact
3380 if(qinfo->SrcSpell)
3382 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
3383 if(!spellInfo)
3385 sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
3386 qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
3387 qinfo->SrcSpell = 0; // quest can't be done for this requirement
3389 else if(!SpellMgr::IsSpellValid(spellInfo))
3391 sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
3392 qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
3393 qinfo->SrcSpell = 0; // quest can't be done for this requirement
3397 for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
3399 uint32 id = qinfo->ReqItemId[j];
3400 if(id)
3402 if(qinfo->ReqItemCount[j]==0)
3404 sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
3405 qinfo->GetQuestId(),j+1,id,j+1);
3406 // no changes, quest can't be done for this requirement
3409 qinfo->SetFlag(QUEST_MANGOS_FLAGS_DELIVER);
3411 if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3413 sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3414 qinfo->GetQuestId(),j+1,id,id);
3415 qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3418 else if(qinfo->ReqItemCount[j]>0)
3420 sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
3421 qinfo->GetQuestId(),j+1,j+1,qinfo->ReqItemCount[j]);
3422 qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
3426 for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
3428 uint32 id = qinfo->ReqSourceId[j];
3429 if(id)
3431 if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3433 sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
3434 qinfo->GetQuestId(),j+1,id,id);
3435 // no changes, quest can't be done for this requirement
3438 else
3440 if(qinfo->ReqSourceCount[j]>0)
3442 sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
3443 qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]);
3444 // no changes, quest ignore this data
3449 for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
3451 uint32 id = qinfo->ReqSpell[j];
3452 if(id)
3454 SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
3455 if(!spellInfo)
3457 sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
3458 qinfo->GetQuestId(),j+1,id,id);
3459 continue;
3462 if(!qinfo->ReqCreatureOrGOId[j])
3464 bool found = false;
3465 for(int k = 0; k < 3; ++k)
3467 if( spellInfo->Effect[k]==SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k])==qinfo->QuestId ||
3468 spellInfo->Effect[k]==SPELL_EFFECT_SEND_EVENT)
3470 found = true;
3471 break;
3475 if(found)
3477 if(!qinfo->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
3479 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_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.",spellInfo->Id,qinfo->QuestId,j+1,j+1);
3481 // this will prevent quest completing without objective
3482 const_cast<Quest*>(qinfo)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
3485 else
3487 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.",
3488 qinfo->GetQuestId(),j+1,id,j+1,id);
3489 // no changes, quest can't be done for this requirement
3495 for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
3497 int32 id = qinfo->ReqCreatureOrGOId[j];
3498 if(id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
3500 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
3501 qinfo->GetQuestId(),j+1,id,uint32(-id));
3502 qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3505 if(id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
3507 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
3508 qinfo->GetQuestId(),j+1,id,uint32(id));
3509 qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
3512 if(id)
3514 // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
3516 qinfo->SetFlag(QUEST_MANGOS_FLAGS_KILL_OR_CAST | QUEST_MANGOS_FLAGS_SPEAKTO);
3518 if(!qinfo->ReqCreatureOrGOCount[j])
3520 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
3521 qinfo->GetQuestId(),j+1,id,j+1);
3522 // no changes, quest can be incorrectly done, but we already report this
3525 else if(qinfo->ReqCreatureOrGOCount[j]>0)
3527 sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
3528 qinfo->GetQuestId(),j+1,j+1,qinfo->ReqCreatureOrGOCount[j]);
3529 // no changes, quest ignore this data
3533 for(int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j )
3535 uint32 id = qinfo->RewChoiceItemId[j];
3536 if(id)
3538 if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3540 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
3541 qinfo->GetQuestId(),j+1,id,id);
3542 qinfo->RewChoiceItemId[j] = 0; // no changes, quest will not reward this
3545 if(!qinfo->RewChoiceItemCount[j])
3547 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
3548 qinfo->GetQuestId(),j+1,id,j+1);
3549 // no changes, quest can't be done
3552 else if(qinfo->RewChoiceItemCount[j]>0)
3554 sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
3555 qinfo->GetQuestId(),j+1,j+1,qinfo->RewChoiceItemCount[j]);
3556 // no changes, quest ignore this data
3560 for(int j = 0; j < QUEST_REWARDS_COUNT; ++j )
3562 uint32 id = qinfo->RewItemId[j];
3563 if(id)
3565 if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3567 sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
3568 qinfo->GetQuestId(),j+1,id,id);
3569 qinfo->RewItemId[j] = 0; // no changes, quest will not reward this item
3572 if(!qinfo->RewItemCount[j])
3574 sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
3575 qinfo->GetQuestId(),j+1,id,j+1);
3576 // no changes
3579 else if(qinfo->RewItemCount[j]>0)
3581 sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
3582 qinfo->GetQuestId(),j+1,j+1,qinfo->RewItemCount[j]);
3583 // no changes, quest ignore this data
3587 for(int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
3589 if(qinfo->RewRepFaction[j])
3591 if(!qinfo->RewRepValue[j])
3593 sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but `RewRepValue%d` = 0, quest will not reward this reputation.",
3594 qinfo->GetQuestId(),j+1,qinfo->RewRepValue[j],j+1);
3595 // no changes
3598 if(!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
3600 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.",
3601 qinfo->GetQuestId(),j+1,qinfo->RewRepFaction[j] ,qinfo->RewRepFaction[j] );
3602 qinfo->RewRepFaction[j] = 0; // quest will not reward this
3605 else if(qinfo->RewRepValue[j]!=0)
3607 sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %u.",
3608 qinfo->GetQuestId(),j+1,j+1,qinfo->RewRepValue[j]);
3609 // no changes, quest ignore this data
3613 if(qinfo->RewSpell)
3615 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
3617 if(!spellInfo)
3619 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
3620 qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
3621 qinfo->RewSpell = 0; // no spell reward will display for this quest
3624 if(!SpellMgr::IsSpellValid(spellInfo))
3626 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest will not have a spell reward.",
3627 qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
3628 qinfo->RewSpell = 0; // no spell reward will display for this quest
3631 if(GetTalentSpellCost(qinfo->RewSpell))
3633 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
3634 qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
3635 qinfo->RewSpell = 0; // no spell reward will display for this quest
3636 continue;
3640 if(qinfo->RewSpellCast)
3642 SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
3644 if(!spellInfo)
3646 sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
3647 qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
3648 qinfo->RewSpellCast = 0; // no spell will be casted on player
3651 if(!SpellMgr::IsSpellValid(spellInfo))
3653 sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest will not have a spell reward.",
3654 qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
3655 qinfo->RewSpellCast = 0; // no spell will be casted on player
3658 if(GetTalentSpellCost(qinfo->RewSpellCast))
3660 sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
3661 qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
3662 qinfo->RewSpellCast = 0; // no spell will be casted on player
3663 continue;
3667 if(qinfo->RewMailTemplateId)
3669 if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
3671 sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template %u does not exist, quest will not have a mail reward.",
3672 qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
3673 qinfo->RewMailTemplateId = 0; // no mail will send to player
3674 qinfo->RewMailDelaySecs = 0; // no mail will send to player
3678 if(qinfo->NextQuestInChain)
3680 QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
3681 if(qNextItr == mQuestTemplates.end())
3683 sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
3684 qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
3685 qinfo->NextQuestInChain = 0;
3687 else
3688 qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
3691 // fill additional data stores
3692 if(qinfo->PrevQuestId)
3694 if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
3696 sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
3698 else
3700 qinfo->prevQuests.push_back(qinfo->PrevQuestId);
3704 if(qinfo->NextQuestId)
3706 QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId()));
3707 if (qNextItr == mQuestTemplates.end())
3709 sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
3711 else
3713 int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
3714 qNextItr->second->prevQuests.push_back(signedQuestId);
3718 if(qinfo->ExclusiveGroup)
3719 mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
3720 if(qinfo->LimitTime)
3721 qinfo->SetFlag(QUEST_MANGOS_FLAGS_TIMED);
3724 // check QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
3725 for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
3727 SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
3728 if(!spellInfo)
3729 continue;
3731 for(int j = 0; j < 3; ++j)
3733 if(spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
3734 continue;
3736 uint32 quest_id = spellInfo->EffectMiscValue[j];
3738 Quest const* quest = GetQuestTemplate(quest_id);
3740 // some quest referenced in spells not exist (outdated spells)
3741 if(!quest)
3742 continue;
3744 if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
3746 sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.",spellInfo->Id,quest_id);
3748 // this will prevent quest completing without objective
3749 const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
3754 sLog.outString();
3755 sLog.outString( ">> Loaded %lu quests definitions", (unsigned long)mQuestTemplates.size() );
3758 void ObjectMgr::LoadQuestLocales()
3760 mQuestLocaleMap.clear(); // need for reload case
3762 QueryResult *result = WorldDatabase.Query("SELECT entry,"
3763 "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
3764 "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
3765 "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
3766 "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
3767 "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
3768 "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
3769 "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
3770 "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
3771 " FROM locales_quest"
3774 if(!result)
3776 barGoLink bar(1);
3778 bar.step();
3780 sLog.outString();
3781 sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
3782 return;
3785 barGoLink bar(result->GetRowCount());
3789 Field *fields = result->Fetch();
3790 bar.step();
3792 uint32 entry = fields[0].GetUInt32();
3794 QuestLocale& data = mQuestLocaleMap[entry];
3796 for(int i = 1; i < MAX_LOCALE; ++i)
3798 std::string str = fields[1+10*(i-1)].GetCppString();
3799 if(!str.empty())
3801 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3802 if(idx >= 0)
3804 if(data.Title.size() <= idx)
3805 data.Title.resize(idx+1);
3807 data.Title[idx] = str;
3810 str = fields[1+10*(i-1)+1].GetCppString();
3811 if(!str.empty())
3813 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3814 if(idx >= 0)
3816 if(data.Details.size() <= idx)
3817 data.Details.resize(idx+1);
3819 data.Details[idx] = str;
3822 str = fields[1+10*(i-1)+2].GetCppString();
3823 if(!str.empty())
3825 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3826 if(idx >= 0)
3828 if(data.Objectives.size() <= idx)
3829 data.Objectives.resize(idx+1);
3831 data.Objectives[idx] = str;
3834 str = fields[1+10*(i-1)+3].GetCppString();
3835 if(!str.empty())
3837 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3838 if(idx >= 0)
3840 if(data.OfferRewardText.size() <= idx)
3841 data.OfferRewardText.resize(idx+1);
3843 data.OfferRewardText[idx] = str;
3846 str = fields[1+10*(i-1)+4].GetCppString();
3847 if(!str.empty())
3849 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3850 if(idx >= 0)
3852 if(data.RequestItemsText.size() <= idx)
3853 data.RequestItemsText.resize(idx+1);
3855 data.RequestItemsText[idx] = str;
3858 str = fields[1+10*(i-1)+5].GetCppString();
3859 if(!str.empty())
3861 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3862 if(idx >= 0)
3864 if(data.EndText.size() <= idx)
3865 data.EndText.resize(idx+1);
3867 data.EndText[idx] = str;
3870 for(int k = 0; k < 4; ++k)
3872 str = fields[1+10*(i-1)+6+k].GetCppString();
3873 if(!str.empty())
3875 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3876 if(idx >= 0)
3878 if(data.ObjectiveText[k].size() <= idx)
3879 data.ObjectiveText[k].resize(idx+1);
3881 data.ObjectiveText[k][idx] = str;
3886 } while (result->NextRow());
3888 delete result;
3890 sLog.outString();
3891 sLog.outString( ">> Loaded %lu Quest locale strings", (unsigned long)mQuestLocaleMap.size() );
3894 void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
3896 if(sWorld.IsScriptScheduled()) // function don't must be called in time scripts use.
3897 return;
3899 sLog.outString( "%s :", tablename);
3901 scripts.clear(); // need for reload support
3903 QueryResult *result = WorldDatabase.PQuery( "SELECT id,delay,command,datalong,datalong2,dataint, x, y, z, o FROM %s", tablename );
3905 uint32 count = 0;
3907 if( !result )
3909 barGoLink bar( 1 );
3910 bar.step();
3912 sLog.outString();
3913 sLog.outString( ">> Loaded %u script definitions", count );
3914 return;
3917 barGoLink bar( result->GetRowCount() );
3921 bar.step();
3923 Field *fields = result->Fetch();
3924 ScriptInfo tmp;
3925 tmp.id = fields[0].GetUInt32();
3926 tmp.delay = fields[1].GetUInt32();
3927 tmp.command = fields[2].GetUInt32();
3928 tmp.datalong = fields[3].GetUInt32();
3929 tmp.datalong2 = fields[4].GetUInt32();
3930 tmp.dataint = fields[5].GetInt32();
3931 tmp.x = fields[6].GetFloat();
3932 tmp.y = fields[7].GetFloat();
3933 tmp.z = fields[8].GetFloat();
3934 tmp.o = fields[9].GetFloat();
3936 // generic command args check
3937 switch(tmp.command)
3939 case SCRIPT_COMMAND_TALK:
3941 if(tmp.datalong > 3)
3943 sLog.outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.datalong,tmp.id);
3944 continue;
3946 if(tmp.dataint==0)
3948 sLog.outErrorDb("Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.dataint,tmp.id);
3949 continue;
3951 if(tmp.dataint < MIN_DB_SCRIPT_STRING_ID || tmp.dataint >= MAX_DB_SCRIPT_STRING_ID)
3953 sLog.outErrorDb("Table `%s` has out of range text id (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.dataint,MIN_DB_SCRIPT_STRING_ID,MAX_DB_SCRIPT_STRING_ID,tmp.id);
3954 continue;
3957 // if(!objmgr.GetMangosStringLocale(tmp.dataint)) will checked after db_script_string loading
3958 break;
3961 case SCRIPT_COMMAND_EMOTE:
3963 if(!sEmotesStore.LookupEntry(tmp.datalong))
3965 sLog.outErrorDb("Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u",tablename,tmp.datalong,tmp.id);
3966 continue;
3968 break;
3971 case SCRIPT_COMMAND_TELEPORT_TO:
3973 if(!sMapStore.LookupEntry(tmp.datalong))
3975 sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.datalong,tmp.id);
3976 continue;
3979 if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
3981 sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.x,tmp.y,tmp.id);
3982 continue;
3984 break;
3987 case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
3989 if(!MaNGOS::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
3991 sLog.outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.x,tmp.y,tmp.id);
3992 continue;
3995 if(!GetCreatureTemplate(tmp.datalong))
3997 sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.datalong,tmp.id);
3998 continue;
4000 break;
4003 case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
4005 GameObjectData const* data = GetGOData(tmp.datalong);
4006 if(!data)
4008 sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,tmp.id);
4009 continue;
4012 GameObjectInfo const* info = GetGameObjectInfo(data->id);
4013 if(!info)
4015 sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,data->id,tmp.id);
4016 continue;
4019 if( info->type==GAMEOBJECT_TYPE_FISHINGNODE ||
4020 info->type==GAMEOBJECT_TYPE_FISHINGHOLE ||
4021 info->type==GAMEOBJECT_TYPE_DOOR ||
4022 info->type==GAMEOBJECT_TYPE_BUTTON ||
4023 info->type==GAMEOBJECT_TYPE_TRAP )
4025 sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,info->id,tmp.id);
4026 continue;
4028 break;
4030 case SCRIPT_COMMAND_OPEN_DOOR:
4031 case SCRIPT_COMMAND_CLOSE_DOOR:
4033 GameObjectData const* data = GetGOData(tmp.datalong);
4034 if(!data)
4036 sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u",tablename,tmp.datalong,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
4037 continue;
4040 GameObjectInfo const* info = GetGameObjectInfo(data->id);
4041 if(!info)
4043 sLog.outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u",tablename,tmp.datalong,data->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
4044 continue;
4047 if( info->type!=GAMEOBJECT_TYPE_DOOR)
4049 sLog.outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u",tablename,info->id,(tmp.command==SCRIPT_COMMAND_OPEN_DOOR ? "SCRIPT_COMMAND_OPEN_DOOR" : "SCRIPT_COMMAND_CLOSE_DOOR"),tmp.id);
4050 continue;
4053 break;
4055 case SCRIPT_COMMAND_QUEST_EXPLORED:
4057 Quest const* quest = GetQuestTemplate(tmp.datalong);
4058 if(!quest)
4060 sLog.outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",tablename,tmp.datalong,tmp.id);
4061 continue;
4064 if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
4066 sLog.outErrorDb("Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",tablename,tmp.datalong,tmp.id);
4068 // this will prevent quest completing without objective
4069 const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
4071 // continue; - quest objective requirement set and command can be allowed
4074 if(float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
4076 sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u",
4077 tablename,tmp.datalong2,tmp.id);
4078 continue;
4081 if(tmp.datalong2 && float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
4083 sLog.outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %f or 0 for disable distance check",
4084 tablename,tmp.datalong2,tmp.id,DEFAULT_VISIBILITY_DISTANCE);
4085 continue;
4088 if(tmp.datalong2 && float(tmp.datalong2) < INTERACTION_DISTANCE)
4090 sLog.outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %f or 0 for disable distance check",
4091 tablename,tmp.datalong2,tmp.id,INTERACTION_DISTANCE);
4092 continue;
4095 break;
4098 case SCRIPT_COMMAND_REMOVE_AURA:
4100 if(!sSpellStore.LookupEntry(tmp.datalong))
4102 sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u",
4103 tablename,tmp.datalong,tmp.id);
4104 continue;
4106 if(tmp.datalong2 & ~0x1) // 1 bits (0,1)
4108 sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u)i n SCRIPT_COMMAND_CAST_SPELL for script id %u",
4109 tablename,tmp.datalong2,tmp.id);
4110 continue;
4112 break;
4114 case SCRIPT_COMMAND_CAST_SPELL:
4116 if(!sSpellStore.LookupEntry(tmp.datalong))
4118 sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u",
4119 tablename,tmp.datalong,tmp.id);
4120 continue;
4122 if(tmp.datalong2 & ~0x3) // 2 bits
4124 sLog.outErrorDb("Table `%s` using unknown flags in datalong2 (%u)i n SCRIPT_COMMAND_CAST_SPELL for script id %u",
4125 tablename,tmp.datalong2,tmp.id);
4126 continue;
4128 break;
4132 if (scripts.find(tmp.id) == scripts.end())
4134 ScriptMap emptyMap;
4135 scripts[tmp.id] = emptyMap;
4137 scripts[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
4139 ++count;
4140 } while( result->NextRow() );
4142 delete result;
4144 sLog.outString();
4145 sLog.outString( ">> Loaded %u script definitions", count );
4148 void ObjectMgr::LoadGameObjectScripts()
4150 LoadScripts(sGameObjectScripts, "gameobject_scripts");
4152 // check ids
4153 for(ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
4155 if(!GetGOData(itr->first))
4156 sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id",itr->first);
4160 void ObjectMgr::LoadQuestEndScripts()
4162 LoadScripts(sQuestEndScripts, "quest_end_scripts");
4164 // check ids
4165 for(ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
4167 if(!GetQuestTemplate(itr->first))
4168 sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id",itr->first);
4172 void ObjectMgr::LoadQuestStartScripts()
4174 LoadScripts(sQuestStartScripts,"quest_start_scripts");
4176 // check ids
4177 for(ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
4179 if(!GetQuestTemplate(itr->first))
4180 sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id",itr->first);
4184 void ObjectMgr::LoadSpellScripts()
4186 LoadScripts(sSpellScripts, "spell_scripts");
4188 // check ids
4189 for(ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
4191 SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
4193 if(!spellInfo)
4195 sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id",itr->first);
4196 continue;
4199 //check for correct spellEffect
4200 bool found = false;
4201 for(int i=0; i<3; ++i)
4203 // skip empty effects
4204 if( !spellInfo->Effect[i] )
4205 continue;
4207 if( spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT )
4209 found = true;
4210 break;
4214 if(!found)
4215 sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect",itr->first,SPELL_EFFECT_SCRIPT_EFFECT);
4219 void ObjectMgr::LoadEventScripts()
4221 LoadScripts(sEventScripts, "event_scripts");
4223 std::set<uint32> evt_scripts;
4224 // Load all possible script entries from gameobjects
4225 for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
4227 GameObjectInfo const * goInfo = sGOStorage.LookupEntry<GameObjectInfo>(i);
4228 if (goInfo)
4230 switch(goInfo->type)
4232 case GAMEOBJECT_TYPE_GOOBER:
4233 if (goInfo->goober.eventId)
4234 evt_scripts.insert(goInfo->goober.eventId);
4235 break;
4236 case GAMEOBJECT_TYPE_CHEST:
4237 if (goInfo->chest.eventId)
4238 evt_scripts.insert(goInfo->chest.eventId);
4239 break;
4240 case GAMEOBJECT_TYPE_CAMERA:
4241 if (goInfo->camera.eventID)
4242 evt_scripts.insert(goInfo->camera.eventID);
4243 default:
4244 break;
4248 // Load all possible script entries from spells
4249 for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
4251 SpellEntry const * spell = sSpellStore.LookupEntry(i);
4252 if (spell)
4254 for(int j=0; j<3; ++j)
4256 if( spell->Effect[j] == SPELL_EFFECT_SEND_EVENT )
4258 if (spell->EffectMiscValue[j])
4259 evt_scripts.insert(spell->EffectMiscValue[j]);
4264 // Then check if all scripts are in above list of possible script entries
4265 for(ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
4267 std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
4268 if (itr2 == evt_scripts.end())
4269 sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect %u",
4270 itr->first, SPELL_EFFECT_SEND_EVENT);
4274 void ObjectMgr::LoadItemTexts()
4276 QueryResult *result = CharacterDatabase.Query("SELECT id, text FROM item_text");
4278 uint32 count = 0;
4280 if( !result )
4282 barGoLink bar( 1 );
4283 bar.step();
4285 sLog.outString();
4286 sLog.outString( ">> Loaded %u item pages", count );
4287 return;
4290 barGoLink bar( result->GetRowCount() );
4292 Field* fields;
4295 bar.step();
4297 fields = result->Fetch();
4299 mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
4301 ++count;
4303 } while ( result->NextRow() );
4305 delete result;
4307 sLog.outString();
4308 sLog.outString( ">> Loaded %u item texts", count );
4311 void ObjectMgr::LoadPageTexts()
4313 sPageTextStore.Free(); // for reload case
4315 sPageTextStore.Load();
4316 sLog.outString( ">> Loaded %u page texts", sPageTextStore.RecordCount );
4317 sLog.outString();
4319 for(uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
4321 // check data correctness
4322 PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
4323 if(!page)
4324 continue;
4326 if(page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
4328 sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i,page->Next_Page);
4329 continue;
4332 // detect circular reference
4333 std::set<uint32> checkedPages;
4334 for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
4336 if(!pageItr->Next_Page)
4337 break;
4338 checkedPages.insert(pageItr->Page_ID);
4339 if(checkedPages.find(pageItr->Next_Page)!=checkedPages.end())
4341 std::ostringstream ss;
4342 ss<< "The text page(s) ";
4343 for (std::set<uint32>::iterator itr= checkedPages.begin();itr!=checkedPages.end(); ++itr)
4344 ss << *itr << " ";
4345 ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
4346 << pageItr->Page_ID <<" to 0";
4347 sLog.outErrorDb(ss.str().c_str());
4348 const_cast<PageText*>(pageItr)->Next_Page = 0;
4349 break;
4355 void ObjectMgr::LoadPageTextLocales()
4357 mPageTextLocaleMap.clear(); // need for reload case
4359 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");
4361 if(!result)
4363 barGoLink bar(1);
4365 bar.step();
4367 sLog.outString();
4368 sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
4369 return;
4372 barGoLink bar(result->GetRowCount());
4376 Field *fields = result->Fetch();
4377 bar.step();
4379 uint32 entry = fields[0].GetUInt32();
4381 PageTextLocale& data = mPageTextLocaleMap[entry];
4383 for(int i = 1; i < MAX_LOCALE; ++i)
4385 std::string str = fields[i].GetCppString();
4386 if(str.empty())
4387 continue;
4389 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4390 if(idx >= 0)
4392 if(data.Text.size() <= idx)
4393 data.Text.resize(idx+1);
4395 data.Text[idx] = str;
4399 } while (result->NextRow());
4401 delete result;
4403 sLog.outString();
4404 sLog.outString( ">> Loaded %lu PageText locale strings", (unsigned long)mPageTextLocaleMap.size() );
4407 struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader>
4409 template<class D>
4410 void convert_from_str(uint32 /*field_pos*/, char *src, D &dst)
4412 dst = D(objmgr.GetScriptId(src));
4416 void ObjectMgr::LoadInstanceTemplate()
4418 SQLInstanceLoader loader;
4419 loader.Load(sInstanceTemplate);
4421 for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
4423 InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
4424 if(!temp) continue;
4425 const MapEntry* entry = sMapStore.LookupEntry(temp->map);
4426 if(!entry)
4428 sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
4429 continue;
4431 else if(!entry->HasResetTime())
4432 continue;
4434 //FIXME: now exist heroic instance, normal/heroic raid instances
4435 // entry->resetTimeHeroic store reset time for both heroic mode instance (raid and non-raid)
4436 // entry->resetTimeRaid store reset time for normal raid only
4437 // for current state entry->resetTimeRaid == entry->resetTimeHeroic in case raid instances with heroic mode.
4438 // but at some point wee need implement reset time dependent from raid instance mode
4439 if(temp->reset_delay == 0)
4441 // use defaults from the DBC
4442 if(entry->resetTimeHeroic) // for both raid and non raids, read above
4444 temp->reset_delay = entry->resetTimeHeroic / DAY;
4446 else if (entry->resetTimeRaid && entry->map_type == MAP_RAID)
4447 // for normal raid only
4449 temp->reset_delay = entry->resetTimeRaid / DAY;
4453 // the reset_delay must be at least one day
4454 temp->reset_delay = std::max((uint32)1, (uint32)(temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME)));
4457 sLog.outString( ">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount );
4458 sLog.outString();
4461 GossipText const *ObjectMgr::GetGossipText(uint32 Text_ID) const
4463 GossipTextMap::const_iterator itr = mGossipText.find(Text_ID);
4464 if(itr != mGossipText.end())
4465 return &itr->second;
4466 return NULL;
4469 void ObjectMgr::LoadGossipText()
4471 QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" );
4473 int count = 0;
4474 if( !result )
4476 barGoLink bar( 1 );
4477 bar.step();
4479 sLog.outString();
4480 sLog.outString( ">> Loaded %u npc texts", count );
4481 return;
4484 int cic;
4486 barGoLink bar( result->GetRowCount() );
4490 ++count;
4491 cic = 0;
4493 Field *fields = result->Fetch();
4495 bar.step();
4497 uint32 Text_ID = fields[cic++].GetUInt32();
4498 if(!Text_ID)
4500 sLog.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore.");
4501 continue;
4504 GossipText& gText = mGossipText[Text_ID];
4506 for (int i=0; i< 8; i++)
4508 gText.Options[i].Text_0 = fields[cic++].GetCppString();
4509 gText.Options[i].Text_1 = fields[cic++].GetCppString();
4511 gText.Options[i].Language = fields[cic++].GetUInt32();
4512 gText.Options[i].Probability = fields[cic++].GetFloat();
4514 for(int j=0; j < 3; ++j)
4516 gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt32();
4517 gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt32();
4520 } while( result->NextRow() );
4522 sLog.outString();
4523 sLog.outString( ">> Loaded %u npc texts", count );
4524 delete result;
4527 void ObjectMgr::LoadNpcTextLocales()
4529 mNpcTextLocaleMap.clear(); // need for reload case
4531 QueryResult *result = WorldDatabase.Query("SELECT entry,"
4532 "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,"
4533 "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,"
4534 "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,"
4535 "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,"
4536 "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,"
4537 "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,"
4538 "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, "
4539 "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 "
4540 " FROM locales_npc_text");
4542 if(!result)
4544 barGoLink bar(1);
4546 bar.step();
4548 sLog.outString();
4549 sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
4550 return;
4553 barGoLink bar(result->GetRowCount());
4557 Field *fields = result->Fetch();
4558 bar.step();
4560 uint32 entry = fields[0].GetUInt32();
4562 NpcTextLocale& data = mNpcTextLocaleMap[entry];
4564 for(int i=1; i<MAX_LOCALE; ++i)
4566 for(int j=0; j<8; ++j)
4568 std::string str0 = fields[1+8*2*(i-1)+2*j].GetCppString();
4569 if(!str0.empty())
4571 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4572 if(idx >= 0)
4574 if(data.Text_0[j].size() <= idx)
4575 data.Text_0[j].resize(idx+1);
4577 data.Text_0[j][idx] = str0;
4580 std::string str1 = fields[1+8*2*(i-1)+2*j+1].GetCppString();
4581 if(!str1.empty())
4583 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4584 if(idx >= 0)
4586 if(data.Text_1[j].size() <= idx)
4587 data.Text_1[j].resize(idx+1);
4589 data.Text_1[j][idx] = str1;
4594 } while (result->NextRow());
4596 delete result;
4598 sLog.outString();
4599 sLog.outString( ">> Loaded %lu NpcText locale strings", (unsigned long)mNpcTextLocaleMap.size() );
4602 //not very fast function but it is called only once a day, or on starting-up
4603 void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
4605 time_t basetime = time(NULL);
4606 sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
4607 //delete all old mails without item and without body immediately, if starting server
4608 if (!serverUp)
4609 CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" UI64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
4610 // 0 1 2 3 4 5 6 7 8 9
4611 QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" UI64FMTD "'", (uint64)basetime);
4612 if ( !result )
4614 barGoLink bar(1);
4615 bar.step();
4616 sLog.outString();
4617 sLog.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty.");
4618 return; // any mails need to be returned or deleted
4621 //std::ostringstream delitems, delmails; //will be here for optimization
4622 //bool deletemail = false, deleteitem = false;
4623 //delitems << "DELETE FROM item_instance WHERE guid IN ( ";
4624 //delmails << "DELETE FROM mail WHERE id IN ( "
4626 barGoLink bar( result->GetRowCount() );
4627 uint32 count = 0;
4628 Field *fields;
4632 bar.step();
4634 fields = result->Fetch();
4635 Mail *m = new Mail;
4636 m->messageID = fields[0].GetUInt32();
4637 m->messageType = fields[1].GetUInt8();
4638 m->sender = fields[2].GetUInt32();
4639 m->receiver = fields[3].GetUInt32();
4640 m->itemTextId = fields[4].GetUInt32();
4641 bool has_items = fields[5].GetBool();
4642 m->expire_time = (time_t)fields[6].GetUInt64();
4643 m->deliver_time = 0;
4644 m->COD = fields[7].GetUInt32();
4645 m->checked = fields[8].GetUInt32();
4646 m->mailTemplateId = fields[9].GetInt16();
4648 Player *pl = 0;
4649 if (serverUp)
4650 pl = GetPlayer((uint64)m->receiver);
4651 if (pl && pl->m_mailsLoaded)
4652 { //this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
4653 //his in mailbox and he has already listed his mails )
4654 delete m;
4655 continue;
4657 //delete or return mail:
4658 if (has_items)
4660 QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
4661 if(resultItems)
4665 Field *fields2 = resultItems->Fetch();
4667 uint32 item_guid_low = fields2[0].GetUInt32();
4668 uint32 item_template = fields2[1].GetUInt32();
4670 m->AddItem(item_guid_low, item_template);
4672 while (resultItems->NextRow());
4674 delete resultItems;
4676 //if it is mail from AH, it shouldn't be returned, but deleted
4677 if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_AUCTION | MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
4679 // mail open and then not returned
4680 for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
4681 CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
4683 else
4685 //mail will be returned:
4686 CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" UI64FMTD "', deliver_time = '" UI64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
4687 delete m;
4688 continue;
4692 if (m->itemTextId)
4693 CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
4695 //deletemail = true;
4696 //delmails << m->messageID << ", ";
4697 CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
4698 delete m;
4699 ++count;
4700 } while (result->NextRow());
4701 delete result;
4703 sLog.outString();
4704 sLog.outString( ">> Loaded %u mails", count );
4707 void ObjectMgr::LoadQuestAreaTriggers()
4709 mQuestAreaTriggerMap.clear(); // need for reload case
4711 QueryResult *result = WorldDatabase.Query( "SELECT id,quest FROM areatrigger_involvedrelation" );
4713 uint32 count = 0;
4715 if( !result )
4717 barGoLink bar( 1 );
4718 bar.step();
4720 sLog.outString();
4721 sLog.outString( ">> Loaded %u quest trigger points", count );
4722 return;
4725 barGoLink bar( result->GetRowCount() );
4729 ++count;
4730 bar.step();
4732 Field *fields = result->Fetch();
4734 uint32 trigger_ID = fields[0].GetUInt32();
4735 uint32 quest_ID = fields[1].GetUInt32();
4737 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
4738 if(!atEntry)
4740 sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",trigger_ID);
4741 continue;
4744 Quest const* quest = GetQuestTemplate(quest_ID);
4746 if(!quest)
4748 sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u",trigger_ID,quest_ID);
4749 continue;
4752 if(!quest->HasFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT))
4754 sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.",trigger_ID,quest_ID);
4756 // this will prevent quest completing without objective
4757 const_cast<Quest*>(quest)->SetFlag(QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT);
4759 // continue; - quest modified to required objective and trigger can be allowed.
4762 mQuestAreaTriggerMap[trigger_ID] = quest_ID;
4764 } while( result->NextRow() );
4766 delete result;
4768 sLog.outString();
4769 sLog.outString( ">> Loaded %u quest trigger points", count );
4772 void ObjectMgr::LoadTavernAreaTriggers()
4774 mTavernAreaTriggerSet.clear(); // need for reload case
4776 QueryResult *result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
4778 uint32 count = 0;
4780 if( !result )
4782 barGoLink bar( 1 );
4783 bar.step();
4785 sLog.outString();
4786 sLog.outString( ">> Loaded %u tavern triggers", count );
4787 return;
4790 barGoLink bar( result->GetRowCount() );
4794 ++count;
4795 bar.step();
4797 Field *fields = result->Fetch();
4799 uint32 Trigger_ID = fields[0].GetUInt32();
4801 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4802 if(!atEntry)
4804 sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
4805 continue;
4808 mTavernAreaTriggerSet.insert(Trigger_ID);
4809 } while( result->NextRow() );
4811 delete result;
4813 sLog.outString();
4814 sLog.outString( ">> Loaded %u tavern triggers", count );
4817 void ObjectMgr::LoadAreaTriggerScripts()
4819 mAreaTriggerScripts.clear(); // need for reload case
4820 QueryResult *result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
4822 uint32 count = 0;
4824 if( !result )
4826 barGoLink bar( 1 );
4827 bar.step();
4829 sLog.outString();
4830 sLog.outString( ">> Loaded %u areatrigger scripts", count );
4831 return;
4834 barGoLink bar( result->GetRowCount() );
4838 ++count;
4839 bar.step();
4841 Field *fields = result->Fetch();
4843 uint32 Trigger_ID = fields[0].GetUInt32();
4844 const char *scriptName = fields[1].GetString();
4846 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4847 if(!atEntry)
4849 sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
4850 continue;
4852 mAreaTriggerScripts[Trigger_ID] = GetScriptId(scriptName);
4853 } while( result->NextRow() );
4855 delete result;
4857 sLog.outString();
4858 sLog.outString( ">> Loaded %u areatrigger scripts", count );
4861 uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team )
4863 bool found = false;
4864 float dist;
4865 uint32 id = 0;
4867 for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
4869 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
4870 if(!node || node->map_id != mapid || !node->MountCreatureID[team == ALLIANCE ? 1 : 0])
4871 continue;
4873 uint8 field = (uint8)((i - 1) / 32);
4874 uint32 submask = 1<<((i-1)%32);
4876 // skip not taxi network nodes
4877 if((sTaxiNodesMask[field] & submask)==0)
4878 continue;
4880 float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
4881 if(found)
4883 if(dist2 < dist)
4885 dist = dist2;
4886 id = i;
4889 else
4891 found = true;
4892 dist = dist2;
4893 id = i;
4897 return id;
4900 void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost)
4902 TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
4903 if(src_i==sTaxiPathSetBySource.end())
4905 path = 0;
4906 cost = 0;
4907 return;
4910 TaxiPathSetForSource& pathSet = src_i->second;
4912 TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
4913 if(dest_i==pathSet.end())
4915 path = 0;
4916 cost = 0;
4917 return;
4920 cost = dest_i->second.price;
4921 path = dest_i->second.ID;
4924 uint32 ObjectMgr::GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_alt_team /* = false */)
4926 uint16 mount_entry = 0;
4928 // select mount creature id
4929 TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
4930 if(node)
4932 if (team == ALLIANCE)
4934 mount_entry = node->MountCreatureID[1];
4935 if(!mount_entry && allowed_alt_team)
4936 mount_entry = node->MountCreatureID[0];
4938 else if (team == HORDE)
4940 mount_entry = node->MountCreatureID[0];
4942 if(!mount_entry && allowed_alt_team)
4943 mount_entry = node->MountCreatureID[1];
4947 CreatureInfo const *mount_info = GetCreatureTemplate(mount_entry);
4948 if (!mount_info)
4949 return 0;
4951 uint16 mount_id = objmgr.ChooseDisplayId(team,mount_info);
4952 if (!mount_id)
4953 return 0;
4955 CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(mount_id);
4956 if (minfo)
4957 mount_id = minfo->modelid;
4959 return mount_id;
4962 void ObjectMgr::GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds)
4964 if(path >= sTaxiPathNodesByPath.size())
4965 return;
4967 TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
4969 pathnodes.Resize(nodeList.size());
4970 mapIds.resize(nodeList.size());
4972 for(size_t i = 0; i < nodeList.size(); ++i)
4974 pathnodes[ i ].x = nodeList[i].x;
4975 pathnodes[ i ].y = nodeList[i].y;
4976 pathnodes[ i ].z = nodeList[i].z;
4978 mapIds[i] = nodeList[i].mapid;
4982 void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
4984 if(path >= sTaxiPathNodesByPath.size())
4985 return;
4987 TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
4989 pathnodes.Resize(nodeList.size());
4991 for(size_t i = 0; i < nodeList.size(); ++i)
4993 pathnodes[ i ].mapid = nodeList[i].mapid;
4994 pathnodes[ i ].x = nodeList[i].x;
4995 pathnodes[ i ].y = nodeList[i].y;
4996 pathnodes[ i ].z = nodeList[i].z;
4997 pathnodes[ i ].actionFlag = nodeList[i].actionFlag;
4998 pathnodes[ i ].delay = nodeList[i].delay;
5002 void ObjectMgr::LoadGraveyardZones()
5004 mGraveYardMap.clear(); // need for reload case
5006 QueryResult *result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
5008 uint32 count = 0;
5010 if( !result )
5012 barGoLink bar( 1 );
5013 bar.step();
5015 sLog.outString();
5016 sLog.outString( ">> Loaded %u graveyard-zone links", count );
5017 return;
5020 barGoLink bar( result->GetRowCount() );
5024 ++count;
5025 bar.step();
5027 Field *fields = result->Fetch();
5029 uint32 safeLocId = fields[0].GetUInt32();
5030 uint32 zoneId = fields[1].GetUInt32();
5031 uint32 team = fields[2].GetUInt32();
5033 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
5034 if(!entry)
5036 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",safeLocId);
5037 continue;
5040 AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
5041 if(!areaEntry)
5043 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.",zoneId);
5044 continue;
5047 if(areaEntry->zone != 0)
5049 sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.",zoneId);
5050 continue;
5053 if(team!=0 && team!=HORDE && team!=ALLIANCE)
5055 sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.",team);
5056 continue;
5059 if(!AddGraveYardLink(safeLocId,zoneId,team,false))
5060 sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.",safeLocId,zoneId);
5061 } while( result->NextRow() );
5063 delete result;
5065 sLog.outString();
5066 sLog.outString( ">> Loaded %u graveyard-zone links", count );
5069 WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
5071 // search for zone associated closest graveyard
5072 uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y,z);
5074 // Simulate std. algorithm:
5075 // found some graveyard associated to (ghost_zone,ghost_map)
5077 // if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
5078 // then check faction
5079 // if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
5080 // then check faction
5081 GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
5082 GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
5083 if(graveLow==graveUp)
5085 sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
5086 return NULL;
5089 // at corpse map
5090 bool foundNear = false;
5091 float distNear;
5092 WorldSafeLocsEntry const* entryNear = NULL;
5094 // at entrance map for corpse map
5095 bool foundEntr = false;
5096 float distEntr;
5097 WorldSafeLocsEntry const* entryEntr = NULL;
5099 // some where other
5100 WorldSafeLocsEntry const* entryFar = NULL;
5102 MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
5104 for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
5106 GraveYardData const& data = itr->second;
5108 WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
5109 if(!entry)
5111 sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",data.safeLocId);
5112 continue;
5115 // skip enemy faction graveyard
5116 // team == 0 case can be at call from .neargrave
5117 if(data.team != 0 && team != 0 && data.team != team)
5118 continue;
5120 // find now nearest graveyard at other map
5121 if(MapId != entry->map_id)
5123 // if find graveyard at different map from where entrance placed (or no entrance data), use any first
5124 if (!mapEntry ||
5125 mapEntry->entrance_map < 0 ||
5126 mapEntry->entrance_map != entry->map_id ||
5127 (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0))
5129 // not have any corrdinates for check distance anyway
5130 entryFar = entry;
5131 continue;
5134 // at entrance map calculate distance (2D);
5135 float dist2 = (entry->x - mapEntry->entrance_x)*(entry->x - mapEntry->entrance_x)
5136 +(entry->y - mapEntry->entrance_y)*(entry->y - mapEntry->entrance_y);
5137 if(foundEntr)
5139 if(dist2 < distEntr)
5141 distEntr = dist2;
5142 entryEntr = entry;
5145 else
5147 foundEntr = true;
5148 distEntr = dist2;
5149 entryEntr = entry;
5152 // find now nearest graveyard at same map
5153 else
5155 float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
5156 if(foundNear)
5158 if(dist2 < distNear)
5160 distNear = dist2;
5161 entryNear = entry;
5164 else
5166 foundNear = true;
5167 distNear = dist2;
5168 entryNear = entry;
5173 if(entryNear)
5174 return entryNear;
5176 if(entryEntr)
5177 return entryEntr;
5179 return entryFar;
5182 GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
5184 GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
5185 GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
5187 for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
5189 if(itr->second.safeLocId==id)
5190 return &itr->second;
5193 return NULL;
5196 bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
5198 if(FindGraveYardData(id,zoneId))
5199 return false;
5201 // add link to loaded data
5202 GraveYardData data;
5203 data.safeLocId = id;
5204 data.team = team;
5206 mGraveYardMap.insert(GraveYardMap::value_type(zoneId,data));
5208 // add link to DB
5209 if(inDB)
5211 WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) "
5212 "VALUES ('%u', '%u','%u')",id,zoneId,team);
5215 return true;
5218 void ObjectMgr::LoadAreaTriggerTeleports()
5220 mAreaTriggers.clear(); // need for reload case
5222 uint32 count = 0;
5224 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
5225 QueryResult *result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_quest_done_heroic, required_failed_text, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
5226 if( !result )
5229 barGoLink bar( 1 );
5231 bar.step();
5233 sLog.outString();
5234 sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
5235 return;
5238 barGoLink bar( result->GetRowCount() );
5242 Field *fields = result->Fetch();
5244 bar.step();
5246 ++count;
5248 uint32 Trigger_ID = fields[0].GetUInt32();
5250 AreaTrigger at;
5252 at.requiredLevel = fields[1].GetUInt8();
5253 at.requiredItem = fields[2].GetUInt32();
5254 at.requiredItem2 = fields[3].GetUInt32();
5255 at.heroicKey = fields[4].GetUInt32();
5256 at.heroicKey2 = fields[5].GetUInt32();
5257 at.requiredQuest = fields[6].GetUInt32();
5258 at.requiredQuestHeroic = fields[7].GetUInt32();
5259 at.requiredFailedText = fields[8].GetCppString();
5260 at.target_mapId = fields[9].GetUInt32();
5261 at.target_X = fields[10].GetFloat();
5262 at.target_Y = fields[11].GetFloat();
5263 at.target_Z = fields[12].GetFloat();
5264 at.target_Orientation = fields[13].GetFloat();
5266 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
5267 if(!atEntry)
5269 sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
5270 continue;
5273 if(at.requiredItem)
5275 ItemPrototype const *pProto = GetItemPrototype(at.requiredItem);
5276 if(!pProto)
5278 sLog.outError("Key item %u does not exist for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
5279 at.requiredItem = 0;
5282 if(at.requiredItem2)
5284 ItemPrototype const *pProto = GetItemPrototype(at.requiredItem2);
5285 if(!pProto)
5287 sLog.outError("Second item %u not exist for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
5288 at.requiredItem2 = 0;
5292 if(at.heroicKey)
5294 ItemPrototype const *pProto = GetItemPrototype(at.heroicKey);
5295 if(!pProto)
5297 sLog.outError("Heroic key item %u not exist for trigger %u, remove key requirement.", at.heroicKey, Trigger_ID);
5298 at.heroicKey = 0;
5302 if(at.heroicKey2)
5304 ItemPrototype const *pProto = GetItemPrototype(at.heroicKey2);
5305 if(!pProto)
5307 sLog.outError("Heroic second key item %u not exist for trigger %u, remove key requirement.", at.heroicKey2, Trigger_ID);
5308 at.heroicKey2 = 0;
5312 if(at.requiredQuest)
5314 QuestMap::iterator qReqItr = mQuestTemplates.find(at.requiredQuest);
5315 if(qReqItr == mQuestTemplates.end())
5317 sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",at.requiredQuest,Trigger_ID);
5318 at.requiredQuest = 0;
5322 if(at.requiredQuestHeroic)
5324 QuestMap::iterator qReqItr = mQuestTemplates.find(at.requiredQuestHeroic);
5325 if(qReqItr == mQuestTemplates.end())
5327 sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",at.requiredQuestHeroic,Trigger_ID);
5328 at.requiredQuestHeroic = 0;
5332 MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
5333 if(!mapEntry)
5335 sLog.outErrorDb("Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Trigger_ID,at.target_mapId);
5336 continue;
5339 if(at.target_X==0 && at.target_Y==0 && at.target_Z==0)
5341 sLog.outErrorDb("Area trigger (ID:%u) target coordinates not provided.",Trigger_ID);
5342 continue;
5345 mAreaTriggers[Trigger_ID] = at;
5347 } while( result->NextRow() );
5349 delete result;
5351 sLog.outString();
5352 sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
5356 * Searches for the areatrigger which teleports players out of the given map
5358 AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
5360 const MapEntry *mapEntry = sMapStore.LookupEntry(Map);
5361 if(!mapEntry) return NULL;
5362 for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
5364 if(itr->second.target_mapId == mapEntry->entrance_map)
5366 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
5367 if(atEntry && atEntry->mapid == Map)
5368 return &itr->second;
5371 return NULL;
5375 * Searches for the areatrigger which teleports players to the given map
5377 AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
5379 for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
5381 if(itr->second.target_mapId == Map)
5383 AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
5384 if(atEntry)
5385 return &itr->second;
5388 return NULL;
5391 void ObjectMgr::SetHighestGuids()
5393 QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" );
5394 if( result )
5396 m_hiCharGuid = (*result)[0].GetUInt32()+1;
5397 delete result;
5400 result = WorldDatabase.Query( "SELECT MAX(guid) FROM creature" );
5401 if( result )
5403 m_hiCreatureGuid = (*result)[0].GetUInt32()+1;
5404 delete result;
5407 result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" );
5408 if( result )
5410 m_hiItemGuid = (*result)[0].GetUInt32()+1;
5411 delete result;
5414 // Cleanup other tables from not existed guids (>=m_hiItemGuid)
5415 CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_hiItemGuid);
5416 CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_hiItemGuid);
5417 CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", m_hiItemGuid);
5418 CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_hiItemGuid);
5420 result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject" );
5421 if( result )
5423 m_hiGoGuid = (*result)[0].GetUInt32()+1;
5424 delete result;
5427 result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse" );
5428 if( result )
5430 m_auctionid = (*result)[0].GetUInt32()+1;
5431 delete result;
5434 result = CharacterDatabase.Query( "SELECT MAX(id) FROM mail" );
5435 if( result )
5437 m_mailid = (*result)[0].GetUInt32()+1;
5438 delete result;
5441 result = CharacterDatabase.Query( "SELECT MAX(id) FROM item_text" );
5442 if( result )
5444 m_ItemTextId = (*result)[0].GetUInt32()+1;
5445 delete result;
5448 result = CharacterDatabase.Query( "SELECT MAX(guid) FROM corpse" );
5449 if( result )
5451 m_hiCorpseGuid = (*result)[0].GetUInt32()+1;
5452 delete result;
5455 result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
5456 if (result)
5458 m_arenaTeamId = (*result)[0].GetUInt32()+1;
5459 delete result;
5462 result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
5463 if (result)
5465 m_equipmentSetGuid = (*result)[0].GetUInt64()+1;
5466 delete result;
5469 result = CharacterDatabase.Query( "SELECT MAX(guildid) FROM guild" );
5470 if (result)
5472 m_guildId = (*result)[0].GetUInt32()+1;
5473 delete result;
5477 uint32 ObjectMgr::GenerateArenaTeamId()
5479 if(m_arenaTeamId>=0xFFFFFFFE)
5481 sLog.outError("Arena team ids overflow!! Can't continue, shutting down server. ");
5482 World::StopNow(ERROR_EXIT_CODE);
5484 return m_arenaTeamId++;
5487 uint32 ObjectMgr::GenerateAuctionID()
5489 if(m_auctionid>=0xFFFFFFFE)
5491 sLog.outError("Auctions ids overflow!! Can't continue, shutting down server. ");
5492 World::StopNow(ERROR_EXIT_CODE);
5494 return m_auctionid++;
5497 uint64 ObjectMgr::GenerateEquipmentSetGuid()
5499 if(m_equipmentSetGuid>=0xFFFFFFFFFFFFFFFEll)
5501 sLog.outError("EquipmentSet guid overflow!! Can't continue, shutting down server. ");
5502 World::StopNow(ERROR_EXIT_CODE);
5504 return m_equipmentSetGuid++;
5507 uint32 ObjectMgr::GenerateGuildId()
5509 if(m_guildId>=0xFFFFFFFE)
5511 sLog.outError("Guild ids overflow!! Can't continue, shutting down server. ");
5512 World::StopNow(ERROR_EXIT_CODE);
5514 return m_guildId++;
5517 uint32 ObjectMgr::GenerateMailID()
5519 if(m_mailid>=0xFFFFFFFE)
5521 sLog.outError("Mail ids overflow!! Can't continue, shutting down server. ");
5522 World::StopNow(ERROR_EXIT_CODE);
5524 return m_mailid++;
5527 uint32 ObjectMgr::GenerateItemTextID()
5529 if(m_ItemTextId>=0xFFFFFFFE)
5531 sLog.outError("Item text ids overflow!! Can't continue, shutting down server. ");
5532 World::StopNow(ERROR_EXIT_CODE);
5534 return m_ItemTextId++;
5537 uint32 ObjectMgr::CreateItemText(std::string text)
5539 uint32 newItemTextId = GenerateItemTextID();
5540 //insert new itempage to container
5541 mItemTexts[ newItemTextId ] = text;
5542 //save new itempage
5543 CharacterDatabase.escape_string(text);
5544 //any Delete query needed, itemTextId is maximum of all ids
5545 std::ostringstream query;
5546 query << "INSERT INTO item_text (id,text) VALUES ( '" << newItemTextId << "', '" << text << "')";
5547 CharacterDatabase.Execute(query.str().c_str()); //needs to be run this way, because mail body may be more than 1024 characters
5548 return newItemTextId;
5551 uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
5553 switch(guidhigh)
5555 case HIGHGUID_ITEM:
5556 if(m_hiItemGuid>=0xFFFFFFFE)
5558 sLog.outError("Item guid overflow!! Can't continue, shutting down server. ");
5559 World::StopNow(ERROR_EXIT_CODE);
5561 return m_hiItemGuid++;
5562 case HIGHGUID_UNIT:
5563 if(m_hiCreatureGuid>=0x00FFFFFE)
5565 sLog.outError("Creature guid overflow!! Can't continue, shutting down server. ");
5566 World::StopNow(ERROR_EXIT_CODE);
5568 return m_hiCreatureGuid++;
5569 case HIGHGUID_PET:
5570 if(m_hiPetGuid>=0x00FFFFFE)
5572 sLog.outError("Pet guid overflow!! Can't continue, shutting down server. ");
5573 World::StopNow(ERROR_EXIT_CODE);
5575 return m_hiPetGuid++;
5576 case HIGHGUID_VEHICLE:
5577 if(m_hiVehicleGuid>=0x00FFFFFF)
5579 sLog.outError("Vehicle guid overflow!! Can't continue, shutting down server. ");
5580 World::StopNow(ERROR_EXIT_CODE);
5582 return m_hiVehicleGuid++;
5583 case HIGHGUID_PLAYER:
5584 if(m_hiCharGuid>=0xFFFFFFFE)
5586 sLog.outError("Players guid overflow!! Can't continue, shutting down server. ");
5587 World::StopNow(ERROR_EXIT_CODE);
5589 return m_hiCharGuid++;
5590 case HIGHGUID_GAMEOBJECT:
5591 if(m_hiGoGuid>=0x00FFFFFE)
5593 sLog.outError("Gameobject guid overflow!! Can't continue, shutting down server. ");
5594 World::StopNow(ERROR_EXIT_CODE);
5596 return m_hiGoGuid++;
5597 case HIGHGUID_CORPSE:
5598 if(m_hiCorpseGuid>=0xFFFFFFFE)
5600 sLog.outError("Corpse guid overflow!! Can't continue, shutting down server. ");
5601 World::StopNow(ERROR_EXIT_CODE);
5603 return m_hiCorpseGuid++;
5604 case HIGHGUID_DYNAMICOBJECT:
5605 if(m_hiDoGuid>=0xFFFFFFFE)
5607 sLog.outError("DynamicObject guid overflow!! Can't continue, shutting down server. ");
5608 World::StopNow(ERROR_EXIT_CODE);
5610 return m_hiDoGuid++;
5611 default:
5612 ASSERT(0);
5615 ASSERT(0);
5616 return 0;
5619 void ObjectMgr::LoadGameObjectLocales()
5621 mGameObjectLocaleMap.clear(); // need for reload case
5623 QueryResult *result = WorldDatabase.Query("SELECT entry,"
5624 "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
5625 "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
5626 "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
5628 if(!result)
5630 barGoLink bar(1);
5632 bar.step();
5634 sLog.outString();
5635 sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
5636 return;
5639 barGoLink bar(result->GetRowCount());
5643 Field *fields = result->Fetch();
5644 bar.step();
5646 uint32 entry = fields[0].GetUInt32();
5648 GameObjectLocale& data = mGameObjectLocaleMap[entry];
5650 for(int i = 1; i < MAX_LOCALE; ++i)
5652 std::string str = fields[i].GetCppString();
5653 if(!str.empty())
5655 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5656 if(idx >= 0)
5658 if(data.Name.size() <= idx)
5659 data.Name.resize(idx+1);
5661 data.Name[idx] = str;
5666 for(int i = 1; i < MAX_LOCALE; ++i)
5668 std::string str = fields[i+(MAX_LOCALE-1)].GetCppString();
5669 if(!str.empty())
5671 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5672 if(idx >= 0)
5674 if(data.CastBarCaption.size() <= idx)
5675 data.CastBarCaption.resize(idx+1);
5677 data.CastBarCaption[idx] = str;
5682 } while (result->NextRow());
5684 delete result;
5686 sLog.outString();
5687 sLog.outString( ">> Loaded %lu gameobject locale strings", (unsigned long)mGameObjectLocaleMap.size() );
5690 struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader>
5692 template<class D>
5693 void convert_from_str(uint32 /*field_pos*/, char *src, D &dst)
5695 dst = D(objmgr.GetScriptId(src));
5699 inline void CheckGOLockId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
5701 if (sLockStore.LookupEntry(dataN))
5702 return;
5704 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
5705 goInfo->id,goInfo->type,N,goInfo->door.lockId,goInfo->door.lockId);
5708 inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
5710 if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN))
5712 if (trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
5713 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
5714 goInfo->id,goInfo->type,N,dataN,dataN,GAMEOBJECT_TYPE_TRAP);
5716 /* disable check for while (too many error reports baout not existed in trap templates
5717 else
5718 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
5719 goInfo->id,goInfo->type,N,dataN,dataN);
5723 inline void CheckGOSpellId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
5725 if (sSpellStore.LookupEntry(dataN))
5726 return;
5728 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
5729 goInfo->id,goInfo->type,N,dataN,dataN);
5732 inline void CheckAndFixGOChairHeightId(GameObjectInfo const* goInfo,uint32 const& dataN,uint32 N)
5734 if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR) )
5735 return;
5737 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
5738 goInfo->id,goInfo->type,N,dataN,UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR);
5740 // prevent client and server unexpected work
5741 const_cast<uint32&>(dataN) = 0;
5744 inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
5746 // 0/1 correct values
5747 if (dataN <= 1)
5748 return;
5750 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.",
5751 goInfo->id,goInfo->type,N,dataN);
5754 inline void CheckGOConsumable(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
5756 // 0/1 correct values
5757 if (dataN <= 1)
5758 return;
5760 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
5761 goInfo->id,goInfo->type,N,dataN);
5764 void ObjectMgr::LoadGameobjectInfo()
5766 SQLGameObjectLoader loader;
5767 loader.Load(sGOStorage);
5769 // some checks
5770 for(uint32 id = 1; id < sGOStorage.MaxEntry; id++)
5772 GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
5773 if (!goInfo)
5774 continue;
5776 // some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore
5778 switch(goInfo->type)
5780 case GAMEOBJECT_TYPE_DOOR: //0
5782 if (goInfo->door.lockId)
5783 CheckGOLockId(goInfo,goInfo->door.lockId,1);
5784 CheckGONoDamageImmuneId(goInfo,goInfo->door.noDamageImmune,3);
5785 break;
5787 case GAMEOBJECT_TYPE_BUTTON: //1
5789 if (goInfo->button.lockId)
5790 CheckGOLockId(goInfo,goInfo->button.lockId,1);
5791 CheckGONoDamageImmuneId(goInfo,goInfo->button.noDamageImmune,4);
5792 break;
5794 case GAMEOBJECT_TYPE_QUESTGIVER: //2
5796 if (goInfo->questgiver.lockId)
5797 CheckGOLockId(goInfo,goInfo->questgiver.lockId,0);
5798 CheckGONoDamageImmuneId(goInfo,goInfo->questgiver.noDamageImmune,5);
5799 break;
5801 case GAMEOBJECT_TYPE_CHEST: //3
5803 if (goInfo->chest.lockId)
5804 CheckGOLockId(goInfo,goInfo->chest.lockId,0);
5806 CheckGOConsumable(goInfo,goInfo->chest.consumable,3);
5808 if (goInfo->chest.linkedTrapId) // linked trap
5809 CheckGOLinkedTrapId(goInfo,goInfo->chest.linkedTrapId,7);
5810 break;
5812 case GAMEOBJECT_TYPE_TRAP: //6
5814 if (goInfo->trap.lockId)
5815 CheckGOLockId(goInfo,goInfo->trap.lockId,0);
5816 /* disable check for while, too many not existed spells
5817 if (goInfo->trap.spellId) // spell
5818 CheckGOSpellId(goInfo,goInfo->trap.spellId,3);
5820 break;
5822 case GAMEOBJECT_TYPE_CHAIR: //7
5823 CheckAndFixGOChairHeightId(goInfo,goInfo->chair.height,1);
5824 break;
5825 case GAMEOBJECT_TYPE_SPELL_FOCUS: //8
5827 if (goInfo->spellFocus.focusId)
5829 if (!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
5830 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
5831 id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId);
5834 if (goInfo->spellFocus.linkedTrapId) // linked trap
5835 CheckGOLinkedTrapId(goInfo,goInfo->spellFocus.linkedTrapId,2);
5836 break;
5838 case GAMEOBJECT_TYPE_GOOBER: //10
5840 if (goInfo->goober.lockId)
5841 CheckGOLockId(goInfo,goInfo->goober.lockId,0);
5843 CheckGOConsumable(goInfo,goInfo->goober.consumable,3);
5845 if (goInfo->goober.pageId) // pageId
5847 if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
5848 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
5849 id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId);
5851 /* disable check for while, too many not existed spells
5852 if (goInfo->goober.spellId) // spell
5853 CheckGOSpellId(goInfo,goInfo->goober.spellId,10);
5855 CheckGONoDamageImmuneId(goInfo,goInfo->goober.noDamageImmune,11);
5856 if (goInfo->goober.linkedTrapId) // linked trap
5857 CheckGOLinkedTrapId(goInfo,goInfo->goober.linkedTrapId,12);
5858 break;
5860 case GAMEOBJECT_TYPE_AREADAMAGE: //12
5862 if (goInfo->areadamage.lockId)
5863 CheckGOLockId(goInfo,goInfo->areadamage.lockId,0);
5864 break;
5866 case GAMEOBJECT_TYPE_CAMERA: //13
5868 if (goInfo->camera.lockId)
5869 CheckGOLockId(goInfo,goInfo->camera.lockId,0);
5870 break;
5872 case GAMEOBJECT_TYPE_MO_TRANSPORT: //15
5874 if (goInfo->moTransport.taxiPathId)
5876 if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
5877 sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
5878 id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId);
5880 break;
5882 case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
5884 /* disable check for while, too many not existed spells
5885 // always must have spell
5886 CheckGOSpellId(goInfo,goInfo->summoningRitual.spellId,1);
5888 break;
5890 case GAMEOBJECT_TYPE_SPELLCASTER: //22
5892 // always must have spell
5893 CheckGOSpellId(goInfo,goInfo->spellcaster.spellId,0);
5894 break;
5896 case GAMEOBJECT_TYPE_FLAGSTAND: //24
5898 if (goInfo->flagstand.lockId)
5899 CheckGOLockId(goInfo,goInfo->flagstand.lockId,0);
5900 CheckGONoDamageImmuneId(goInfo,goInfo->flagstand.noDamageImmune,5);
5901 break;
5903 case GAMEOBJECT_TYPE_FISHINGHOLE: //25
5905 if (goInfo->fishinghole.lockId)
5906 CheckGOLockId(goInfo,goInfo->fishinghole.lockId,4);
5907 break;
5909 case GAMEOBJECT_TYPE_FLAGDROP: //26
5911 if (goInfo->flagdrop.lockId)
5912 CheckGOLockId(goInfo,goInfo->flagdrop.lockId,0);
5913 CheckGONoDamageImmuneId(goInfo,goInfo->flagdrop.noDamageImmune,3);
5914 break;
5916 case GAMEOBJECT_TYPE_BARBER_CHAIR: //32
5917 CheckAndFixGOChairHeightId(goInfo,goInfo->barberChair.chairheight,0);
5918 break;
5922 sLog.outString( ">> Loaded %u game object templates", sGOStorage.RecordCount );
5923 sLog.outString();
5926 void ObjectMgr::LoadExplorationBaseXP()
5928 uint32 count = 0;
5929 QueryResult *result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
5931 if( !result )
5933 barGoLink bar( 1 );
5935 bar.step();
5937 sLog.outString();
5938 sLog.outString( ">> Loaded %u BaseXP definitions", count );
5939 return;
5942 barGoLink bar( result->GetRowCount() );
5946 bar.step();
5948 Field *fields = result->Fetch();
5949 uint32 level = fields[0].GetUInt32();
5950 uint32 basexp = fields[1].GetUInt32();
5951 mBaseXPTable[level] = basexp;
5952 ++count;
5954 while (result->NextRow());
5956 delete result;
5958 sLog.outString();
5959 sLog.outString( ">> Loaded %u BaseXP definitions", count );
5962 uint32 ObjectMgr::GetBaseXP(uint32 level)
5964 return mBaseXPTable[level] ? mBaseXPTable[level] : 0;
5967 uint32 ObjectMgr::GetXPForLevel(uint32 level)
5969 if (level < mPlayerXPperLevel.size())
5970 return mPlayerXPperLevel[level];
5971 return 0;
5974 void ObjectMgr::LoadPetNames()
5976 uint32 count = 0;
5977 QueryResult *result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
5979 if( !result )
5981 barGoLink bar( 1 );
5983 bar.step();
5985 sLog.outString();
5986 sLog.outString( ">> Loaded %u pet name parts", count );
5987 return;
5990 barGoLink bar( result->GetRowCount() );
5994 bar.step();
5996 Field *fields = result->Fetch();
5997 std::string word = fields[0].GetString();
5998 uint32 entry = fields[1].GetUInt32();
5999 bool half = fields[2].GetBool();
6000 if(half)
6001 PetHalfName1[entry].push_back(word);
6002 else
6003 PetHalfName0[entry].push_back(word);
6004 ++count;
6006 while (result->NextRow());
6007 delete result;
6009 sLog.outString();
6010 sLog.outString( ">> Loaded %u pet name parts", count );
6013 void ObjectMgr::LoadPetNumber()
6015 QueryResult* result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
6016 if(result)
6018 Field *fields = result->Fetch();
6019 m_hiPetNumber = fields[0].GetUInt32()+1;
6020 delete result;
6023 barGoLink bar( 1 );
6024 bar.step();
6026 sLog.outString();
6027 sLog.outString( ">> Loaded the max pet number: %d", m_hiPetNumber-1);
6030 std::string ObjectMgr::GeneratePetName(uint32 entry)
6032 std::vector<std::string> & list0 = PetHalfName0[entry];
6033 std::vector<std::string> & list1 = PetHalfName1[entry];
6035 if(list0.empty() || list1.empty())
6037 CreatureInfo const *cinfo = GetCreatureTemplate(entry);
6038 char* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
6039 if(!petname)
6040 petname = cinfo->Name;
6041 return std::string(petname);
6044 return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
6047 uint32 ObjectMgr::GeneratePetNumber()
6049 return ++m_hiPetNumber;
6052 void ObjectMgr::LoadCorpses()
6054 uint32 count = 0;
6055 // 0 1 2 3 4 5 6 7 8 10
6056 QueryResult *result = CharacterDatabase.Query("SELECT position_x, position_y, position_z, orientation, map, data, time, corpse_type, instance, guid FROM corpse WHERE corpse_type <> 0");
6058 if( !result )
6060 barGoLink bar( 1 );
6062 bar.step();
6064 sLog.outString();
6065 sLog.outString( ">> Loaded %u corpses", count );
6066 return;
6069 barGoLink bar( result->GetRowCount() );
6073 bar.step();
6075 Field *fields = result->Fetch();
6077 uint32 guid = fields[result->GetFieldCount()-1].GetUInt32();
6079 Corpse *corpse = new Corpse;
6080 if(!corpse->LoadFromDB(guid,fields))
6082 delete corpse;
6083 continue;
6086 ObjectAccessor::Instance().AddCorpse(corpse);
6088 ++count;
6090 while (result->NextRow());
6091 delete result;
6093 sLog.outString();
6094 sLog.outString( ">> Loaded %u corpses", count );
6097 void ObjectMgr::LoadReputationOnKill()
6099 uint32 count = 0;
6101 // 0 1 2
6102 QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
6103 // 3 4 5 6 7 8 9
6104 "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
6105 "FROM creature_onkill_reputation");
6107 if(!result)
6109 barGoLink bar(1);
6111 bar.step();
6113 sLog.outString();
6114 sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
6115 return;
6118 barGoLink bar(result->GetRowCount());
6122 Field *fields = result->Fetch();
6123 bar.step();
6125 uint32 creature_id = fields[0].GetUInt32();
6127 ReputationOnKillEntry repOnKill;
6128 repOnKill.repfaction1 = fields[1].GetUInt32();
6129 repOnKill.repfaction2 = fields[2].GetUInt32();
6130 repOnKill.is_teamaward1 = fields[3].GetBool();
6131 repOnKill.reputation_max_cap1 = fields[4].GetUInt32();
6132 repOnKill.repvalue1 = fields[5].GetInt32();
6133 repOnKill.is_teamaward2 = fields[6].GetBool();
6134 repOnKill.reputation_max_cap2 = fields[7].GetUInt32();
6135 repOnKill.repvalue2 = fields[8].GetInt32();
6136 repOnKill.team_dependent = fields[9].GetUInt8();
6138 if(!GetCreatureTemplate(creature_id))
6140 sLog.outErrorDb("Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped",creature_id);
6141 continue;
6144 if(repOnKill.repfaction1)
6146 FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
6147 if(!factionEntry1)
6149 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction1);
6150 continue;
6154 if(repOnKill.repfaction2)
6156 FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
6157 if(!factionEntry2)
6159 sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction2);
6160 continue;
6164 mRepOnKill[creature_id] = repOnKill;
6166 ++count;
6167 } while (result->NextRow());
6169 delete result;
6171 sLog.outString();
6172 sLog.outString(">> Loaded %u creature award reputation definitions", count);
6175 void ObjectMgr::LoadPointsOfInterest()
6177 uint32 count = 0;
6179 // 0 1 2 3 4 5
6180 QueryResult *result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
6182 if(!result)
6184 barGoLink bar(1);
6186 bar.step();
6188 sLog.outString();
6189 sLog.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
6190 return;
6193 barGoLink bar(result->GetRowCount());
6197 Field *fields = result->Fetch();
6198 bar.step();
6200 uint32 point_id = fields[0].GetUInt32();
6202 PointOfInterest POI;
6203 POI.x = fields[1].GetFloat();
6204 POI.y = fields[2].GetFloat();
6205 POI.icon = fields[3].GetUInt32();
6206 POI.flags = fields[4].GetUInt32();
6207 POI.data = fields[5].GetUInt32();
6208 POI.icon_name = fields[6].GetCppString();
6210 if(!MaNGOS::IsValidMapCoord(POI.x,POI.y))
6212 sLog.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.",point_id,POI.x,POI.y);
6213 continue;
6216 mPointsOfInterest[point_id] = POI;
6218 ++count;
6219 } while (result->NextRow());
6221 delete result;
6223 sLog.outString();
6224 sLog.outString(">> Loaded %u Points of Interest definitions", count);
6227 void ObjectMgr::LoadNPCSpellClickSpells()
6229 uint32 count = 0;
6231 mSpellClickInfoMap.clear();
6232 // 0 1 2 3 4 5
6233 QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags FROM npc_spellclick_spells");
6235 if(!result)
6237 barGoLink bar(1);
6239 bar.step();
6241 sLog.outString();
6242 sLog.outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
6243 return;
6246 barGoLink bar(result->GetRowCount());
6250 Field *fields = result->Fetch();
6251 bar.step();
6253 uint32 npc_entry = fields[0].GetUInt32();
6254 CreatureInfo const* cInfo = GetCreatureTemplate(npc_entry);
6255 if (!cInfo)
6257 sLog.outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
6258 continue;
6261 uint32 spellid = fields[1].GetUInt32();
6262 SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid);
6263 if (!spellinfo)
6265 sLog.outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid);
6266 continue;
6269 uint32 quest_start = fields[2].GetUInt32();
6271 // quest might be 0 to enable spellclick independent of any quest
6272 if (quest_start)
6274 if(mQuestTemplates.find(quest_start) == mQuestTemplates.end())
6276 sLog.outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start);
6277 continue;
6282 bool quest_start_active = fields[3].GetBool();
6284 uint32 quest_end = fields[4].GetUInt32();
6285 // quest might be 0 to enable spellclick active infinity after start quest
6286 if (quest_end)
6288 if(mQuestTemplates.find(quest_end) == mQuestTemplates.end())
6290 sLog.outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end);
6291 continue;
6296 uint8 castFlags = fields[5].GetUInt8();
6297 SpellClickInfo info;
6298 info.spellId = spellid;
6299 info.questStart = quest_start;
6300 info.questStartCanActive = quest_start_active;
6301 info.questEnd = quest_end;
6302 info.castFlags = castFlags;
6303 mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info));
6305 // mark creature template as spell clickable
6306 const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK;
6308 ++count;
6309 } while (result->NextRow());
6311 delete result;
6313 sLog.outString();
6314 sLog.outString(">> Loaded %u spellclick definitions", count);
6317 void ObjectMgr::LoadWeatherZoneChances()
6319 uint32 count = 0;
6321 // 0 1 2 3 4 5 6 7 8 9 10 11 12
6322 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");
6324 if(!result)
6326 barGoLink bar(1);
6328 bar.step();
6330 sLog.outString();
6331 sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
6332 return;
6335 barGoLink bar(result->GetRowCount());
6339 Field *fields = result->Fetch();
6340 bar.step();
6342 uint32 zone_id = fields[0].GetUInt32();
6344 WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
6346 for(int season = 0; season < WEATHER_SEASONS; ++season)
6348 wzc.data[season].rainChance = fields[season * (MAX_WEATHER_TYPE-1) + 1].GetUInt32();
6349 wzc.data[season].snowChance = fields[season * (MAX_WEATHER_TYPE-1) + 2].GetUInt32();
6350 wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE-1) + 3].GetUInt32();
6352 if(wzc.data[season].rainChance > 100)
6354 wzc.data[season].rainChance = 25;
6355 sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%%",zone_id,season);
6358 if(wzc.data[season].snowChance > 100)
6360 wzc.data[season].snowChance = 25;
6361 sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%%",zone_id,season);
6364 if(wzc.data[season].stormChance > 100)
6366 wzc.data[season].stormChance = 25;
6367 sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%%",zone_id,season);
6371 ++count;
6372 } while (result->NextRow());
6374 delete result;
6376 sLog.outString();
6377 sLog.outString(">> Loaded %u weather definitions", count);
6380 void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t)
6382 mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
6383 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
6384 if(t)
6385 WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" UI64FMTD "', '%u' )", loguid, uint64(t), instance);
6388 void ObjectMgr::DeleteCreatureData(uint32 guid)
6390 // remove mapid*cellid -> guid_set map
6391 CreatureData const* data = GetCreatureData(guid);
6392 if(data)
6393 RemoveCreatureFromGrid(guid, data);
6395 mCreatureDataMap.erase(guid);
6398 void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t)
6400 mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
6401 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
6402 if(t)
6403 WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" UI64FMTD "', '%u' )", loguid, uint64(t), instance);
6406 void ObjectMgr::DeleteRespawnTimeForInstance(uint32 instance)
6408 RespawnTimes::iterator next;
6410 for(RespawnTimes::iterator itr = mGORespawnTimes.begin(); itr != mGORespawnTimes.end(); itr = next)
6412 next = itr;
6413 ++next;
6415 if(GUID_HIPART(itr->first)==instance)
6416 mGORespawnTimes.erase(itr);
6419 for(RespawnTimes::iterator itr = mCreatureRespawnTimes.begin(); itr != mCreatureRespawnTimes.end(); itr = next)
6421 next = itr;
6422 ++next;
6424 if(GUID_HIPART(itr->first)==instance)
6425 mCreatureRespawnTimes.erase(itr);
6428 WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", instance);
6429 WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", instance);
6432 void ObjectMgr::DeleteGOData(uint32 guid)
6434 // remove mapid*cellid -> guid_set map
6435 GameObjectData const* data = GetGOData(guid);
6436 if(data)
6437 RemoveGameobjectFromGrid(guid, data);
6439 mGameObjectDataMap.erase(guid);
6442 void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
6444 // corpses are always added to spawn mode 0 and they are spawned by their instance id
6445 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
6446 cell_guids.corpses[player_guid] = instance;
6449 void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
6451 // corpses are always added to spawn mode 0 and they are spawned by their instance id
6452 CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
6453 cell_guids.corpses.erase(player_guid);
6456 void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map,char const* table)
6458 map.clear(); // need for reload case
6460 uint32 count = 0;
6462 QueryResult *result = WorldDatabase.PQuery("SELECT id,quest FROM %s",table);
6464 if(!result)
6466 barGoLink bar(1);
6468 bar.step();
6470 sLog.outString();
6471 sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.",table,table);
6472 return;
6475 barGoLink bar(result->GetRowCount());
6479 Field *fields = result->Fetch();
6480 bar.step();
6482 uint32 id = fields[0].GetUInt32();
6483 uint32 quest = fields[1].GetUInt32();
6485 if(mQuestTemplates.find(quest) == mQuestTemplates.end())
6487 sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.",table,quest,id);
6488 continue;
6491 map.insert(QuestRelations::value_type(id,quest));
6493 ++count;
6494 } while (result->NextRow());
6496 delete result;
6498 sLog.outString();
6499 sLog.outString(">> Loaded %u quest relations from %s", count,table);
6502 void ObjectMgr::LoadGameobjectQuestRelations()
6504 LoadQuestRelationsHelper(mGOQuestRelations,"gameobject_questrelation");
6506 for(QuestRelations::iterator itr = mGOQuestRelations.begin(); itr != mGOQuestRelations.end(); ++itr)
6508 GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
6509 if(!goInfo)
6510 sLog.outErrorDb("Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
6511 else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
6512 sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
6516 void ObjectMgr::LoadGameobjectInvolvedRelations()
6518 LoadQuestRelationsHelper(mGOQuestInvolvedRelations,"gameobject_involvedrelation");
6520 for(QuestRelations::iterator itr = mGOQuestInvolvedRelations.begin(); itr != mGOQuestInvolvedRelations.end(); ++itr)
6522 GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
6523 if(!goInfo)
6524 sLog.outErrorDb("Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
6525 else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
6526 sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
6530 void ObjectMgr::LoadCreatureQuestRelations()
6532 LoadQuestRelationsHelper(mCreatureQuestRelations,"creature_questrelation");
6534 for(QuestRelations::iterator itr = mCreatureQuestRelations.begin(); itr != mCreatureQuestRelations.end(); ++itr)
6536 CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
6537 if(!cInfo)
6538 sLog.outErrorDb("Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
6539 else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
6540 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);
6544 void ObjectMgr::LoadCreatureInvolvedRelations()
6546 LoadQuestRelationsHelper(mCreatureQuestInvolvedRelations,"creature_involvedrelation");
6548 for(QuestRelations::iterator itr = mCreatureQuestInvolvedRelations.begin(); itr != mCreatureQuestInvolvedRelations.end(); ++itr)
6550 CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
6551 if(!cInfo)
6552 sLog.outErrorDb("Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
6553 else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
6554 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);
6558 void ObjectMgr::LoadReservedPlayersNames()
6560 m_ReservedNames.clear(); // need for reload case
6562 QueryResult *result = WorldDatabase.Query("SELECT name FROM reserved_name");
6564 uint32 count = 0;
6566 if( !result )
6568 barGoLink bar( 1 );
6569 bar.step();
6571 sLog.outString();
6572 sLog.outString( ">> Loaded %u reserved player names", count );
6573 return;
6576 barGoLink bar( result->GetRowCount() );
6578 Field* fields;
6581 bar.step();
6582 fields = result->Fetch();
6583 std::string name= fields[0].GetCppString();
6585 std::wstring wstr;
6586 if(!Utf8toWStr (name,wstr))
6588 sLog.outError("Table `reserved_name` have invalid name: %s", name.c_str() );
6589 continue;
6592 wstrToLower(wstr);
6594 m_ReservedNames.insert(wstr);
6595 ++count;
6596 } while ( result->NextRow() );
6598 delete result;
6600 sLog.outString();
6601 sLog.outString( ">> Loaded %u reserved player names", count );
6604 bool ObjectMgr::IsReservedName( const std::string& name ) const
6606 std::wstring wstr;
6607 if(!Utf8toWStr (name,wstr))
6608 return false;
6610 wstrToLower(wstr);
6612 return m_ReservedNames.find(wstr) != m_ReservedNames.end();
6615 enum LanguageType
6617 LT_BASIC_LATIN = 0x0000,
6618 LT_EXTENDEN_LATIN = 0x0001,
6619 LT_CYRILLIC = 0x0002,
6620 LT_EAST_ASIA = 0x0004,
6621 LT_ANY = 0xFFFF
6624 static LanguageType GetRealmLanguageType(bool create)
6626 switch(sWorld.getConfig(CONFIG_REALM_ZONE))
6628 case REALM_ZONE_UNKNOWN: // any language
6629 case REALM_ZONE_DEVELOPMENT:
6630 case REALM_ZONE_TEST_SERVER:
6631 case REALM_ZONE_QA_SERVER:
6632 return LT_ANY;
6633 case REALM_ZONE_UNITED_STATES: // extended-Latin
6634 case REALM_ZONE_OCEANIC:
6635 case REALM_ZONE_LATIN_AMERICA:
6636 case REALM_ZONE_ENGLISH:
6637 case REALM_ZONE_GERMAN:
6638 case REALM_ZONE_FRENCH:
6639 case REALM_ZONE_SPANISH:
6640 return LT_EXTENDEN_LATIN;
6641 case REALM_ZONE_KOREA: // East-Asian
6642 case REALM_ZONE_TAIWAN:
6643 case REALM_ZONE_CHINA:
6644 return LT_EAST_ASIA;
6645 case REALM_ZONE_RUSSIAN: // Cyrillic
6646 return LT_CYRILLIC;
6647 default:
6648 return create ? LT_BASIC_LATIN : LT_ANY; // basic-Latin at create, any at login
6652 bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
6654 if(strictMask==0) // any language, ignore realm
6656 if(isExtendedLatinString(wstr,numericOrSpace))
6657 return true;
6658 if(isCyrillicString(wstr,numericOrSpace))
6659 return true;
6660 if(isEastAsianString(wstr,numericOrSpace))
6661 return true;
6662 return false;
6665 if(strictMask & 0x2) // realm zone specific
6667 LanguageType lt = GetRealmLanguageType(create);
6668 if(lt & LT_EXTENDEN_LATIN)
6669 if(isExtendedLatinString(wstr,numericOrSpace))
6670 return true;
6671 if(lt & LT_CYRILLIC)
6672 if(isCyrillicString(wstr,numericOrSpace))
6673 return true;
6674 if(lt & LT_EAST_ASIA)
6675 if(isEastAsianString(wstr,numericOrSpace))
6676 return true;
6679 if(strictMask & 0x1) // basic Latin
6681 if(isBasicLatinString(wstr,numericOrSpace))
6682 return true;
6685 return false;
6688 uint8 ObjectMgr::CheckPlayerName( const std::string& name, bool create )
6690 std::wstring wname;
6691 if(!Utf8toWStr(name,wname))
6692 return CHAR_NAME_INVALID_CHARACTER;
6694 if(wname.size() > MAX_PLAYER_NAME)
6695 return CHAR_NAME_TOO_LONG;
6697 uint32 minName = sWorld.getConfig(CONFIG_MIN_PLAYER_NAME);
6698 if(wname.size() < minName)
6699 return CHAR_NAME_TOO_SHORT;
6701 uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES);
6702 if(!isValidString(wname,strictMask,false,create))
6703 return CHAR_NAME_MIXED_LANGUAGES;
6705 return CHAR_NAME_SUCCESS;
6708 bool ObjectMgr::IsValidCharterName( const std::string& name )
6710 std::wstring wname;
6711 if(!Utf8toWStr(name,wname))
6712 return false;
6714 if(wname.size() > MAX_CHARTER_NAME)
6715 return false;
6717 uint32 minName = sWorld.getConfig(CONFIG_MIN_CHARTER_NAME);
6718 if(wname.size() < minName)
6719 return false;
6721 uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES);
6723 return isValidString(wname,strictMask,true);
6726 PetNameInvalidReason ObjectMgr::CheckPetName( const std::string& name )
6728 std::wstring wname;
6729 if(!Utf8toWStr(name,wname))
6730 return PET_NAME_INVALID;
6732 if(wname.size() > MAX_PET_NAME)
6733 return PET_NAME_TOO_LONG;
6735 uint32 minName = sWorld.getConfig(CONFIG_MIN_PET_NAME);
6736 if(wname.size() < minName)
6737 return PET_NAME_TOO_SHORT;
6739 uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES);
6740 if(!isValidString(wname,strictMask,false))
6741 return PET_NAME_MIXED_LANGUAGES;
6743 return PET_NAME_SUCCESS;
6746 int ObjectMgr::GetIndexForLocale( LocaleConstant loc )
6748 if(loc==LOCALE_enUS)
6749 return -1;
6751 for(size_t i=0;i < m_LocalForIndex.size(); ++i)
6752 if(m_LocalForIndex[i]==loc)
6753 return i;
6755 return -1;
6758 LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
6760 if (i<0 || i>=m_LocalForIndex.size())
6761 return LOCALE_enUS;
6763 return m_LocalForIndex[i];
6766 int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc )
6768 if(loc==LOCALE_enUS)
6769 return -1;
6771 for(size_t i=0;i < m_LocalForIndex.size(); ++i)
6772 if(m_LocalForIndex[i]==loc)
6773 return i;
6775 m_LocalForIndex.push_back(loc);
6776 return m_LocalForIndex.size()-1;
6779 void ObjectMgr::LoadGameObjectForQuests()
6781 mGameObjectForQuestSet.clear(); // need for reload case
6783 if( !sGOStorage.MaxEntry )
6785 barGoLink bar( 1 );
6786 bar.step();
6787 sLog.outString();
6788 sLog.outString( ">> Loaded 0 GameObjects for quests" );
6789 return;
6792 barGoLink bar( sGOStorage.MaxEntry - 1 );
6793 uint32 count = 0;
6795 // collect GO entries for GO that must activated
6796 for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
6798 bar.step();
6799 GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
6800 if(!goInfo)
6801 continue;
6803 switch(goInfo->type)
6805 // scan GO chest with loot including quest items
6806 case GAMEOBJECT_TYPE_CHEST:
6808 uint32 loot_id = goInfo->GetLootId();
6810 // find quest loot for GO
6811 if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
6813 mGameObjectForQuestSet.insert(go_entry);
6814 ++count;
6816 break;
6818 case GAMEOBJECT_TYPE_GOOBER:
6820 if(goInfo->goober.questId) //quests objects
6822 mGameObjectForQuestSet.insert(go_entry);
6823 count++;
6825 break;
6827 default:
6828 break;
6832 sLog.outString();
6833 sLog.outString( ">> Loaded %u GameObjects for quests", count );
6836 bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value)
6838 int32 start_value = min_value;
6839 int32 end_value = max_value;
6840 // some string can have negative indexes range
6841 if (start_value < 0)
6843 if (end_value >= start_value)
6845 sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.",table,min_value,max_value);
6846 return false;
6849 // real range (max+1,min+1) exaple: (-10,-1000) -> -999...-10+1
6850 std::swap(start_value,end_value);
6851 ++start_value;
6852 ++end_value;
6854 else
6856 if (start_value >= end_value)
6858 sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.",table,min_value,max_value);
6859 return false;
6863 // cleanup affected map part for reloading case
6864 for(MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();)
6866 if (itr->first >= start_value && itr->first < end_value)
6867 mMangosStringLocaleMap.erase(itr++);
6868 else
6869 ++itr;
6872 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);
6874 if (!result)
6876 barGoLink bar(1);
6878 bar.step();
6880 sLog.outString();
6881 if (min_value == MIN_MANGOS_STRING_ID) // error only in case internal strings
6882 sLog.outErrorDb(">> Loaded 0 mangos strings. DB table `%s` is empty. Cannot continue.",table);
6883 else
6884 sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.",table);
6885 return false;
6888 uint32 count = 0;
6890 barGoLink bar(result->GetRowCount());
6894 Field *fields = result->Fetch();
6895 bar.step();
6897 int32 entry = fields[0].GetInt32();
6899 if (entry==0)
6901 sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
6902 continue;
6904 else if (entry < start_value || entry >= end_value)
6906 sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,min_value,max_value);
6907 continue;
6910 MangosStringLocale& data = mMangosStringLocaleMap[entry];
6912 if (data.Content.size() > 0)
6914 sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
6915 continue;
6918 data.Content.resize(1);
6919 ++count;
6921 // 0 -> default, idx in to idx+1
6922 data.Content[0] = fields[1].GetCppString();
6924 for(int i = 1; i < MAX_LOCALE; ++i)
6926 std::string str = fields[i+1].GetCppString();
6927 if (!str.empty())
6929 int idx = GetOrNewIndexForLocale(LocaleConstant(i));
6930 if (idx >= 0)
6932 // 0 -> default, idx in to idx+1
6933 if (data.Content.size() <= idx+1)
6934 data.Content.resize(idx+2);
6936 data.Content[idx+1] = str;
6940 } while (result->NextRow());
6942 delete result;
6944 sLog.outString();
6945 if (min_value == MIN_MANGOS_STRING_ID)
6946 sLog.outString( ">> Loaded %u MaNGOS strings from table %s", count,table);
6947 else
6948 sLog.outString( ">> Loaded %u string templates from %s", count,table);
6950 return true;
6953 const char *ObjectMgr::GetMangosString(int32 entry, int locale_idx) const
6955 // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
6956 // Content[0] always exist if exist MangosStringLocale
6957 if(MangosStringLocale const *msl = GetMangosStringLocale(entry))
6959 if(msl->Content.size() > locale_idx+1 && !msl->Content[locale_idx+1].empty())
6960 return msl->Content[locale_idx+1].c_str();
6961 else
6962 return msl->Content[0].c_str();
6965 if(entry > 0)
6966 sLog.outErrorDb("Entry %i not found in `mangos_string` table.",entry);
6967 else
6968 sLog.outErrorDb("Mangos string entry %i not found in DB.",entry);
6969 return "<error>";
6972 void ObjectMgr::LoadFishingBaseSkillLevel()
6974 mFishingBaseForArea.clear(); // for reload case
6976 uint32 count = 0;
6977 QueryResult *result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
6979 if( !result )
6981 barGoLink bar( 1 );
6983 bar.step();
6985 sLog.outString();
6986 sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
6987 return;
6990 barGoLink bar( result->GetRowCount() );
6994 bar.step();
6996 Field *fields = result->Fetch();
6997 uint32 entry = fields[0].GetUInt32();
6998 int32 skill = fields[1].GetInt32();
7000 AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
7001 if(!fArea)
7003 sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist",entry);
7004 continue;
7007 mFishingBaseForArea[entry] = skill;
7008 ++count;
7010 while (result->NextRow());
7012 delete result;
7014 sLog.outString();
7015 sLog.outString( ">> Loaded %u areas for fishing base skill level", count );
7018 // Searches for the same condition already in Conditions store
7019 // Returns Id if found, else adds it to Conditions and returns Id
7020 uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 value2 )
7022 PlayerCondition lc = PlayerCondition(condition, value1, value2);
7023 for (uint16 i=0; i < mConditions.size(); ++i)
7025 if (lc == mConditions[i])
7026 return i;
7029 mConditions.push_back(lc);
7031 if(mConditions.size() > 0xFFFF)
7033 sLog.outError("Conditions store overflow! Current and later loaded conditions will ignored!");
7034 return 0;
7037 return mConditions.size() - 1;
7040 bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names )
7042 for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i)
7044 std::wstring wname;
7045 if(!Utf8toWStr(names.name[i],wname))
7046 return false;
7048 if(mainpart!=GetMainPartOfName(wname,i+1))
7049 return false;
7051 return true;
7054 uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id)
7056 AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(trigger_id);
7057 if(i!= mAreaTriggerScripts.end())
7058 return i->second;
7059 return 0;
7062 // Checks if player meets the condition
7063 bool PlayerCondition::Meets(Player const * player) const
7065 if( !player )
7066 return false; // player not present, return false
7068 switch (condition)
7070 case CONDITION_NONE:
7071 return true; // empty condition, always met
7072 case CONDITION_AURA:
7073 return player->HasAura(value1, value2);
7074 case CONDITION_ITEM:
7075 return player->HasItemCount(value1, value2);
7076 case CONDITION_ITEM_EQUIPPED:
7077 return player->HasItemOrGemWithIdEquipped(value1,1);
7078 case CONDITION_ZONEID:
7079 return player->GetZoneId() == value1;
7080 case CONDITION_REPUTATION_RANK:
7082 FactionEntry const* faction = sFactionStore.LookupEntry(value1);
7083 return faction && player->GetReputationMgr().GetRank(faction) >= value2;
7085 case CONDITION_TEAM:
7086 return player->GetTeam() == value1;
7087 case CONDITION_SKILL:
7088 return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2;
7089 case CONDITION_QUESTREWARDED:
7090 return player->GetQuestRewardStatus(value1);
7091 case CONDITION_QUESTTAKEN:
7093 QuestStatus status = player->GetQuestStatus(value1);
7094 return (status == QUEST_STATUS_INCOMPLETE);
7096 case CONDITION_AD_COMMISSION_AURA:
7098 Unit::AuraMap const& auras = player->GetAuras();
7099 for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
7100 if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual[0]==3580)
7101 return true;
7102 return false;
7104 case CONDITION_NO_AURA:
7105 return !player->HasAura(value1, value2);
7106 case CONDITION_ACTIVE_EVENT:
7107 return gameeventmgr.IsActiveEvent(value1);
7108 default:
7109 return false;
7113 // Verification of condition values validity
7114 bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2)
7116 if( condition >= MAX_CONDITION) // Wrong condition type
7118 sLog.outErrorDb("Condition has bad type of %u, skipped ", condition );
7119 return false;
7122 switch (condition)
7124 case CONDITION_AURA:
7126 if(!sSpellStore.LookupEntry(value1))
7128 sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1);
7129 return false;
7131 if(value2 > 2)
7133 sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2);
7134 return false;
7136 break;
7138 case CONDITION_ITEM:
7140 ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
7141 if(!proto)
7143 sLog.outErrorDb("Item condition requires to have non existing item (%u), skipped", value1);
7144 return false;
7146 break;
7148 case CONDITION_ITEM_EQUIPPED:
7150 ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
7151 if(!proto)
7153 sLog.outErrorDb("ItemEquipped condition requires to have non existing item (%u) equipped, skipped", value1);
7154 return false;
7156 break;
7158 case CONDITION_ZONEID:
7160 AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1);
7161 if(!areaEntry)
7163 sLog.outErrorDb("Zone condition requires to be in non existing area (%u), skipped", value1);
7164 return false;
7166 if(areaEntry->zone != 0)
7168 sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", value1);
7169 return false;
7171 break;
7173 case CONDITION_REPUTATION_RANK:
7175 FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
7176 if(!factionEntry)
7178 sLog.outErrorDb("Reputation condition requires to have reputation non existing faction (%u), skipped", value1);
7179 return false;
7181 break;
7183 case CONDITION_TEAM:
7185 if (value1 != ALLIANCE && value1 != HORDE)
7187 sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", value1);
7188 return false;
7190 break;
7192 case CONDITION_SKILL:
7194 SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1);
7195 if (!pSkill)
7197 sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", value1);
7198 return false;
7200 if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue() )
7202 sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", value2);
7203 return false;
7205 break;
7207 case CONDITION_QUESTREWARDED:
7208 case CONDITION_QUESTTAKEN:
7210 Quest const *Quest = objmgr.GetQuestTemplate(value1);
7211 if (!Quest)
7213 sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", value1);
7214 return false;
7216 if(value2)
7217 sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
7218 break;
7220 case CONDITION_AD_COMMISSION_AURA:
7222 if(value1)
7223 sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", value1);
7224 if(value2)
7225 sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
7226 break;
7228 case CONDITION_NO_AURA:
7230 if(!sSpellStore.LookupEntry(value1))
7232 sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1);
7233 return false;
7235 if(value2 > 2)
7237 sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2);
7238 return false;
7240 break;
7242 case CONDITION_ACTIVE_EVENT:
7244 GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap();
7245 if(value1 >=events.size() || !events[value1].isValid())
7247 sLog.outErrorDb("Active event condition requires existed event id (%u), skipped", value1);
7248 return false;
7250 break;
7252 case CONDITION_NONE:
7253 break;
7255 return true;
7258 SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
7260 switch(pSkill->categoryId)
7262 case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE;
7263 case SKILL_CATEGORY_WEAPON:
7264 if(pSkill->id!=SKILL_FIST_WEAPONS)
7265 return SKILL_RANGE_LEVEL;
7266 else
7267 return SKILL_RANGE_MONO;
7268 case SKILL_CATEGORY_ARMOR:
7269 case SKILL_CATEGORY_CLASS:
7270 if(pSkill->id != SKILL_LOCKPICKING)
7271 return SKILL_RANGE_MONO;
7272 else
7273 return SKILL_RANGE_LEVEL;
7274 case SKILL_CATEGORY_SECONDARY:
7275 case SKILL_CATEGORY_PROFESSION:
7276 // not set skills for professions and racial abilities
7277 if(IsProfessionSkill(pSkill->id))
7278 return SKILL_RANGE_RANK;
7279 else if(racial)
7280 return SKILL_RANGE_NONE;
7281 else
7282 return SKILL_RANGE_MONO;
7283 default:
7284 case SKILL_CATEGORY_ATTRIBUTES: //not found in dbc
7285 case SKILL_CATEGORY_GENERIC: //only GENERIC(DND)
7286 return SKILL_RANGE_NONE;
7290 void ObjectMgr::LoadGameTele()
7292 m_GameTeleMap.clear(); // for reload case
7294 uint32 count = 0;
7295 QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
7297 if( !result )
7299 barGoLink bar( 1 );
7301 bar.step();
7303 sLog.outString();
7304 sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
7305 return;
7308 barGoLink bar( result->GetRowCount() );
7312 bar.step();
7314 Field *fields = result->Fetch();
7316 uint32 id = fields[0].GetUInt32();
7318 GameTele gt;
7320 gt.position_x = fields[1].GetFloat();
7321 gt.position_y = fields[2].GetFloat();
7322 gt.position_z = fields[3].GetFloat();
7323 gt.orientation = fields[4].GetFloat();
7324 gt.mapId = fields[5].GetUInt32();
7325 gt.name = fields[6].GetCppString();
7327 if(!MapManager::IsValidMapCoord(gt.mapId,gt.position_x,gt.position_y,gt.position_z,gt.orientation))
7329 sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.",id,gt.name.c_str());
7330 continue;
7333 if(!Utf8toWStr(gt.name,gt.wnameLow))
7335 sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.",id);
7336 continue;
7339 wstrToLower( gt.wnameLow );
7341 m_GameTeleMap[id] = gt;
7343 ++count;
7345 while (result->NextRow());
7346 delete result;
7348 sLog.outString();
7349 sLog.outString( ">> Loaded %u GameTeleports", count );
7352 GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
7354 // explicit name case
7355 std::wstring wname;
7356 if(!Utf8toWStr(name,wname))
7357 return false;
7359 // converting string that we try to find to lower case
7360 wstrToLower( wname );
7362 // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
7363 const GameTele* alt = NULL;
7364 for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
7365 if(itr->second.wnameLow == wname)
7366 return &itr->second;
7367 else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
7368 alt = &itr->second;
7370 return alt;
7373 bool ObjectMgr::AddGameTele(GameTele& tele)
7375 // find max id
7376 uint32 new_id = 0;
7377 for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
7378 if(itr->first > new_id)
7379 new_id = itr->first;
7381 // use next
7382 ++new_id;
7384 if(!Utf8toWStr(tele.name,tele.wnameLow))
7385 return false;
7387 wstrToLower( tele.wnameLow );
7389 m_GameTeleMap[new_id] = tele;
7391 return WorldDatabase.PExecuteLog("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%d,'%s')",
7392 new_id,tele.position_x,tele.position_y,tele.position_z,tele.orientation,tele.mapId,tele.name.c_str());
7395 bool ObjectMgr::DeleteGameTele(const std::string& name)
7397 // explicit name case
7398 std::wstring wname;
7399 if(!Utf8toWStr(name,wname))
7400 return false;
7402 // converting string that we try to find to lower case
7403 wstrToLower( wname );
7405 for(GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
7407 if(itr->second.wnameLow == wname)
7409 WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",itr->second.name.c_str());
7410 m_GameTeleMap.erase(itr);
7411 return true;
7415 return false;
7418 void ObjectMgr::LoadTrainerSpell()
7420 // For reload case
7421 for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
7422 itr->second.Clear();
7423 m_mCacheTrainerSpellMap.clear();
7425 std::set<uint32> skip_trainers;
7427 QueryResult *result = WorldDatabase.Query("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM npc_trainer");
7429 if( !result )
7431 barGoLink bar( 1 );
7433 bar.step();
7435 sLog.outString();
7436 sLog.outErrorDb(">> Loaded `npc_trainer`, table is empty!");
7437 return;
7440 barGoLink bar( result->GetRowCount() );
7442 std::set<uint32> talentIds;
7444 uint32 count = 0;
7447 bar.step();
7449 Field* fields = result->Fetch();
7451 uint32 entry = fields[0].GetUInt32();
7452 uint32 spell = fields[1].GetUInt32();
7454 CreatureInfo const* cInfo = GetCreatureTemplate(entry);
7456 if(!cInfo)
7458 sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
7459 continue;
7462 if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
7464 if(skip_trainers.count(entry) == 0)
7466 sLog.outErrorDb("Table `npc_trainer` have data for not creature template (Entry: %u) without trainer flag, ignore", entry);
7467 skip_trainers.insert(entry);
7469 continue;
7472 SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
7473 if(!spellinfo)
7475 sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u ) has non existing spell %u, ignore", entry,spell);
7476 continue;
7479 if(!SpellMgr::IsSpellValid(spellinfo))
7481 sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u) has broken learning spell %u, ignore", entry, spell);
7482 continue;
7485 if(GetTalentSpellCost(spell))
7487 if(talentIds.count(spell)==0)
7489 sLog.outErrorDb("Table `npc_trainer` has talent as learning spell %u, ignore", spell);
7490 talentIds.insert(spell);
7492 continue;
7495 TrainerSpellData& data = m_mCacheTrainerSpellMap[entry];
7497 TrainerSpell& trainerSpell = data.spellList[spell];
7498 trainerSpell.spell = spell;
7499 trainerSpell.spellCost = fields[2].GetUInt32();
7500 trainerSpell.reqSkill = fields[3].GetUInt32();
7501 trainerSpell.reqSkillValue = fields[4].GetUInt32();
7502 trainerSpell.reqLevel = fields[5].GetUInt32();
7504 if(!trainerSpell.reqLevel)
7505 trainerSpell.reqLevel = spellinfo->spellLevel;
7507 // calculate learned spell for profession case when stored cast-spell
7508 trainerSpell.learnedSpell = spell;
7509 for(int i = 0; i <3; ++i)
7511 if(spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL)
7512 continue;
7513 if(SpellMgr::IsProfessionOrRidingSpell(spellinfo->EffectTriggerSpell[i]))
7515 trainerSpell.learnedSpell = spellinfo->EffectTriggerSpell[i];
7516 break;
7520 if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell))
7521 data.trainerType = 2;
7523 ++count;
7525 } while (result->NextRow());
7526 delete result;
7528 sLog.outString();
7529 sLog.outString( ">> Loaded %d Trainers", count );
7532 void ObjectMgr::LoadVendors()
7534 // For reload case
7535 for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
7536 itr->second.Clear();
7537 m_mCacheVendorItemMap.clear();
7539 std::set<uint32> skip_vendors;
7541 QueryResult *result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor");
7542 if( !result )
7544 barGoLink bar( 1 );
7546 bar.step();
7548 sLog.outString();
7549 sLog.outErrorDb(">> Loaded `npc_vendor`, table is empty!");
7550 return;
7553 barGoLink bar( result->GetRowCount() );
7555 uint32 count = 0;
7558 bar.step();
7559 Field* fields = result->Fetch();
7561 uint32 entry = fields[0].GetUInt32();
7562 uint32 item_id = fields[1].GetUInt32();
7563 uint32 maxcount = fields[2].GetUInt32();
7564 uint32 incrtime = fields[3].GetUInt32();
7565 uint32 ExtendedCost = fields[4].GetUInt32();
7567 if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost,NULL,&skip_vendors))
7568 continue;
7570 VendorItemData& vList = m_mCacheVendorItemMap[entry];
7572 vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
7573 ++count;
7575 } while (result->NextRow());
7576 delete result;
7578 sLog.outString();
7579 sLog.outString( ">> Loaded %d Vendors ", count );
7582 void ObjectMgr::LoadNpcTextId()
7585 m_mCacheNpcTextIdMap.clear();
7587 QueryResult* result = WorldDatabase.Query("SELECT npc_guid, textid FROM npc_gossip");
7588 if( !result )
7590 barGoLink bar( 1 );
7592 bar.step();
7594 sLog.outString();
7595 sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
7596 return;
7599 barGoLink bar( result->GetRowCount() );
7601 uint32 count = 0;
7602 uint32 guid,textid;
7605 bar.step();
7607 Field* fields = result->Fetch();
7609 guid = fields[0].GetUInt32();
7610 textid = fields[1].GetUInt32();
7612 if (!GetCreatureData(guid))
7614 sLog.outErrorDb("Table `npc_gossip` have not existed creature (GUID: %u) entry, ignore. ",guid);
7615 continue;
7617 if (!GetGossipText(textid))
7619 sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
7620 continue;
7623 m_mCacheNpcTextIdMap[guid] = textid ;
7624 ++count;
7626 } while (result->NextRow());
7627 delete result;
7629 sLog.outString();
7630 sLog.outString( ">> Loaded %d NpcTextId ", count );
7633 void ObjectMgr::LoadNpcOptions()
7635 m_mCacheNpcOptionList.clear(); // For reload case
7637 QueryResult *result = WorldDatabase.Query(
7638 // 0 1 2 3 4 5 6 7 8
7639 "SELECT id,gossip_id,npcflag,icon,action,box_money,coded,option_text,box_text "
7640 "FROM npc_option");
7642 if( !result )
7644 barGoLink bar( 1 );
7646 bar.step();
7648 sLog.outString();
7649 sLog.outErrorDb(">> Loaded `npc_option`, table is empty!");
7650 return;
7653 barGoLink bar( result->GetRowCount() );
7655 uint32 count = 0;
7659 bar.step();
7661 Field* fields = result->Fetch();
7663 GossipOption go;
7664 go.Id = fields[0].GetUInt32();
7665 go.GossipId = fields[1].GetUInt32();
7666 go.NpcFlag = fields[2].GetUInt32();
7667 go.Icon = fields[3].GetUInt32();
7668 go.Action = fields[4].GetUInt32();
7669 go.BoxMoney = fields[5].GetUInt32();
7670 go.Coded = fields[6].GetUInt8()!=0;
7671 go.OptionText = fields[7].GetCppString();
7672 go.BoxText = fields[8].GetCppString();
7674 m_mCacheNpcOptionList.push_back(go);
7676 ++count;
7678 } while (result->NextRow());
7679 delete result;
7681 sLog.outString();
7682 sLog.outString( ">> Loaded %d npc_option entries", count );
7685 void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )
7687 VendorItemData& vList = m_mCacheVendorItemMap[entry];
7688 vList.AddItem(item,maxcount,incrtime,extendedcost);
7690 WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
7693 bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item )
7695 CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
7696 if(iter == m_mCacheVendorItemMap.end())
7697 return false;
7699 if(!iter->second.FindItem(item))
7700 return false;
7702 iter->second.RemoveItem(item);
7703 WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
7704 return true;
7707 bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set<uint32>* skip_vendors ) const
7709 CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
7710 if(!cInfo)
7712 if(pl)
7713 ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
7714 else
7715 sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
7716 return false;
7719 if(!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
7721 if(!skip_vendors || skip_vendors->count(vendor_entry)==0)
7723 if(pl)
7724 ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
7725 else
7726 sLog.outErrorDb("Table `npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
7728 if(skip_vendors)
7729 skip_vendors->insert(vendor_entry);
7731 return false;
7734 if(!GetItemPrototype(item_id))
7736 if(pl)
7737 ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
7738 else
7739 sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
7740 return false;
7743 if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
7745 if(pl)
7746 ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST,ExtendedCost);
7747 else
7748 sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
7749 return false;
7752 if(maxcount > 0 && incrtime == 0)
7754 if(pl)
7755 ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
7756 else
7757 sLog.outErrorDb( "Table `npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
7758 return false;
7760 else if(maxcount==0 && incrtime > 0)
7762 if(pl)
7763 ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
7764 else
7765 sLog.outErrorDb( "Table `npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
7766 return false;
7769 VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
7770 if(!vItems)
7771 return true; // later checks for non-empty lists
7773 if(vItems->FindItem(item_id))
7775 if(pl)
7776 ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,item_id);
7777 else
7778 sLog.outErrorDb( "Table `npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
7779 return false;
7782 if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
7784 if(pl)
7785 ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
7786 else
7787 sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
7788 return false;
7791 return true;
7794 void ObjectMgr::LoadScriptNames()
7796 m_scriptNames.push_back("");
7797 QueryResult *result = WorldDatabase.Query(
7798 "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
7799 "UNION "
7800 "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
7801 "UNION "
7802 "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' "
7803 "UNION "
7804 "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
7805 "UNION "
7806 "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
7808 if( !result )
7810 barGoLink bar( 1 );
7811 bar.step();
7812 sLog.outString();
7813 sLog.outErrorDb(">> Loaded empty set of Script Names!");
7814 return;
7817 barGoLink bar( result->GetRowCount() );
7818 uint32 count = 0;
7822 bar.step();
7823 m_scriptNames.push_back((*result)[0].GetString());
7824 ++count;
7825 } while (result->NextRow());
7826 delete result;
7828 std::sort(m_scriptNames.begin(), m_scriptNames.end());
7829 sLog.outString();
7830 sLog.outString( ">> Loaded %d Script Names", count );
7833 uint32 ObjectMgr::GetScriptId(const char *name)
7835 // use binary search to find the script name in the sorted vector
7836 // assume "" is the first element
7837 if(!name) return 0;
7838 ScriptNameMap::const_iterator itr =
7839 std::lower_bound(m_scriptNames.begin(), m_scriptNames.end(), name);
7840 if(itr == m_scriptNames.end() || *itr != name) return 0;
7841 return itr - m_scriptNames.begin();
7844 void ObjectMgr::CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids)
7846 for(ScriptMapMap::const_iterator itrMM = scripts.begin(); itrMM != scripts.end(); ++itrMM)
7848 for(ScriptMap::const_iterator itrM = itrMM->second.begin(); itrM != itrMM->second.end(); ++itrM)
7850 switch(itrM->second.command)
7852 case SCRIPT_COMMAND_TALK:
7854 if(!GetMangosStringLocale (itrM->second.dataint))
7855 sLog.outErrorDb( "Table `db_script_string` not has string id %u used db script (ID: %u)", itrM->second.dataint, itrMM->first);
7857 if(ids.count(itrM->second.dataint))
7858 ids.erase(itrM->second.dataint);
7865 void ObjectMgr::LoadDbScriptStrings()
7867 LoadMangosStrings(WorldDatabase,"db_script_string",MIN_DB_SCRIPT_STRING_ID,MAX_DB_SCRIPT_STRING_ID);
7869 std::set<int32> ids;
7871 for(int32 i = MIN_DB_SCRIPT_STRING_ID; i < MAX_DB_SCRIPT_STRING_ID; ++i)
7872 if(GetMangosStringLocale(i))
7873 ids.insert(i);
7875 CheckScripts(sQuestEndScripts,ids);
7876 CheckScripts(sQuestStartScripts,ids);
7877 CheckScripts(sSpellScripts,ids);
7878 CheckScripts(sGameObjectScripts,ids);
7879 CheckScripts(sEventScripts,ids);
7881 WaypointMgr.CheckTextsExistance(ids);
7883 for(std::set<int32>::const_iterator itr = ids.begin(); itr != ids.end(); ++itr)
7884 sLog.outErrorDb( "Table `db_script_string` has unused string id %u", *itr);
7887 // Functions for scripting access
7888 uint32 GetAreaTriggerScriptId(uint32 trigger_id)
7890 return objmgr.GetAreaTriggerScriptId(trigger_id);
7893 bool LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value, int32 end_value)
7895 // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
7896 // start/end reversed for negative values
7897 if (start_value > MAX_DB_SCRIPT_STRING_ID || end_value >= start_value)
7899 sLog.outErrorDb("Table '%s' attempt loaded with reserved by mangos range (%d - %d), strings not loaded.",table,start_value,end_value+1);
7900 return false;
7903 return objmgr.LoadMangosStrings(db,table,start_value,end_value);
7906 uint32 MANGOS_DLL_SPEC GetScriptId(const char *name)
7908 return objmgr.GetScriptId(name);
7911 ObjectMgr::ScriptNameMap & GetScriptNames()
7913 return objmgr.GetScriptNames();
7916 CreatureInfo const* GetCreatureTemplateStore(uint32 entry)
7918 return sCreatureStorage.LookupEntry<CreatureInfo>(entry);
7921 Quest const* GetQuestTemplateStore(uint32 entry)
7923 return objmgr.GetQuestTemplate(entry);