root/trunk/src/game/ObjectMgr.cpp @ 88

Revision 88, 251.9 kB (checked in by yumileroy, 17 years ago)

[svn] * Added some player info cache to the core. Thanx to Rognar for patch, visaglis for testing and bugging me to add it.

Original author: KingPin?
Date: 2008-10-21 12:43:24-05:00

Line 
1/*
2 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
3 *
4 * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "Common.h"
22#include "Database/DatabaseEnv.h"
23#include "Database/SQLStorage.h"
24
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 "WorldSession.h"
32#include "Group.h"
33#include "Guild.h"
34#include "ArenaTeam.h"
35#include "Transports.h"
36#include "ProgressBar.h"
37#include "Policies/SingletonImp.h"
38#include "Language.h"
39#include "GameEvent.h"
40#include "Spell.h"
41#include "Chat.h"
42#include "AccountMgr.h"
43#include "InstanceSaveMgr.h"
44#include "SpellAuras.h"
45#include "Util.h"
46
47INSTANTIATE_SINGLETON_1(ObjectMgr);
48
49ScriptMapMap sQuestEndScripts;
50ScriptMapMap sQuestStartScripts;
51ScriptMapMap sSpellScripts;
52ScriptMapMap sGameObjectScripts;
53ScriptMapMap sEventScripts;
54
55bool normalizePlayerName(std::string& name)
56{
57    if(name.empty())
58        return false;
59
60    wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
61    size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
62
63    if(!Utf8toWStr(name,&wstr_buf[0],wstr_len))
64        return false;
65
66    wstr_buf[0] = wcharToUpper(wstr_buf[0]);
67    for(size_t i = 1; i < wstr_len; ++i)
68        wstr_buf[i] = wcharToLower(wstr_buf[i]);
69
70    if(!WStrToUtf8(wstr_buf,wstr_len,name))
71        return false;
72
73    return true;
74}
75
76LanguageDesc lang_description[LANGUAGES_COUNT] =
77{
78    { LANG_ADDON,           0, 0                       },
79    { LANG_UNIVERSAL,       0, 0                       },
80    { LANG_ORCISH,        669, SKILL_LANG_ORCISH       },
81    { LANG_DARNASSIAN,    671, SKILL_LANG_DARNASSIAN   },
82    { LANG_TAURAHE,       670, SKILL_LANG_TAURAHE      },
83    { LANG_DWARVISH,      672, SKILL_LANG_DWARVEN      },
84    { LANG_COMMON,        668, SKILL_LANG_COMMON       },
85    { LANG_DEMONIC,       815, SKILL_LANG_DEMON_TONGUE },
86    { LANG_TITAN,         816, SKILL_LANG_TITAN        },
87    { LANG_THALASSIAN,    813, SKILL_LANG_THALASSIAN   },
88    { LANG_DRACONIC,      814, SKILL_LANG_DRACONIC     },
89    { LANG_KALIMAG,       817, SKILL_LANG_OLD_TONGUE   },
90    { LANG_GNOMISH,      7340, SKILL_LANG_GNOMISH      },
91    { LANG_TROLL,        7341, SKILL_LANG_TROLL        },
92    { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK  },
93    { LANG_DRAENEI,     29932, SKILL_LANG_DRAENEI      },
94    { LANG_ZOMBIE,          0, 0                       },
95    { LANG_GNOMISH_BINARY,  0, 0                       },
96    { LANG_GOBLIN_BINARY,   0, 0                       }
97};
98
99LanguageDesc const* GetLanguageDescByID(uint32 lang)
100{
101    for(int i = 0; i < LANGUAGES_COUNT; ++i)
102    {
103        if(uint32(lang_description[i].lang_id) == lang)
104            return &lang_description[i];
105    }
106
107    return NULL;
108}
109
110ObjectMgr::ObjectMgr()
111{
112    m_hiCharGuid        = 1;
113    m_hiCreatureGuid    = 1;
114    m_hiPetGuid         = 1;
115    m_hiItemGuid        = 1;
116    m_hiGoGuid          = 1;
117    m_hiDoGuid          = 1;
118    m_hiCorpseGuid      = 1;
119
120    m_hiPetNumber       = 1;
121
122    mGuildBankTabPrice.resize(GUILD_BANK_MAX_TABS);
123    mGuildBankTabPrice[0] = 100;
124    mGuildBankTabPrice[1] = 250;
125    mGuildBankTabPrice[2] = 500;
126    mGuildBankTabPrice[3] = 1000;
127    mGuildBankTabPrice[4] = 2500;
128    mGuildBankTabPrice[5] = 5000;
129
130    // Only zero condition left, others will be added while loading DB tables
131    mConditions.resize(1);
132}
133
134ObjectMgr::~ObjectMgr()
135{
136    for( QuestMap::iterator i = mQuestTemplates.begin( ); i != mQuestTemplates.end( ); ++ i )
137    {
138        delete i->second;
139    }
140    mQuestTemplates.clear( );
141
142    for( GossipTextMap::iterator i = mGossipText.begin( ); i != mGossipText.end( ); ++ i )
143    {
144        delete i->second;
145    }
146    mGossipText.clear( );
147
148    mAreaTriggers.clear();
149
150    for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++ i )
151    {
152        delete[] i->second;
153    }
154    petInfo.clear();
155
156    // free only if loaded
157    for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
158        delete[] playerClassInfo[class_].levelInfo;
159
160    for (int race = 0; race < MAX_RACES; ++race)
161        for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
162            delete[] playerInfo[race][class_].levelInfo;
163
164    // free group and guild objects
165    for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
166        delete (*itr);
167    for (GuildSet::iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
168        delete (*itr);
169
170    for (CachePlayerInfoMap::iterator itr = m_mPlayerInfoMap.begin(); itr != m_mPlayerInfoMap.end(); ++itr)
171        delete itr->second;
172
173    for(ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
174        delete itr->second;
175
176    for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
177        itr->second.Clear();
178
179    for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
180        itr->second.Clear();
181}
182
183void ObjectMgr::LoadPlayerInfoInCache()
184{
185    QueryResult *result = CharacterDatabase.PQuery("SELECT guid, name, data, class  FROM characters");
186    if(!result)
187    {
188        sLog.outError( "ROGNAR LoadPlayerCache");
189        return;
190    }
191
192    PCachePlayerInfo pPPlayerInfo = NULL;
193    Field *fields = NULL;
194    Tokens tdata;
195    barGoLink bar( result->GetRowCount() );
196    do
197    {
198        bar.step();
199        fields = result->Fetch();
200        pPPlayerInfo = new CachePlayerInfo();
201
202        pPPlayerInfo->sPlayerName = fields[1].GetString();
203
204        tdata.clear();
205        tdata = StrSplit(fields[2].GetCppString(), " ");
206       
207        pPPlayerInfo->unLevel = (uint32)atoi(tdata[UNIT_FIELD_LEVEL].c_str());
208        pPPlayerInfo->unfield = (uint32)atoi(tdata[UNIT_FIELD_BYTES_0].c_str());
209
210        pPPlayerInfo->unArenaInfoId0 = (uint32)atoi(tdata[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (0 * 6)].c_str());
211        pPPlayerInfo->unArenaInfoId1 = (uint32)atoi(tdata[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (1 * 6)].c_str());
212        pPPlayerInfo->unArenaInfoId2 = (uint32)atoi(tdata[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (2 * 6)].c_str());
213
214        pPPlayerInfo->unArenaInfoSlot0 = (uint32)atoi(tdata[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 0 * 6 + 5].c_str());
215        pPPlayerInfo->unArenaInfoSlot1 = (uint32)atoi(tdata[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 * 6 + 5].c_str());
216        pPPlayerInfo->unArenaInfoSlot2 = (uint32)atoi(tdata[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 2 * 6 + 5].c_str());
217   
218        pPPlayerInfo->unClass = (uint32)fields[3].GetUInt32();
219        m_mPlayerInfoMap[fields[0].GetUInt32()] = pPPlayerInfo;
220    }
221    while (result->NextRow());
222    delete result;
223
224    sLog.outString();
225    sLog.outString( ">> Loaded info about %d players", m_mPlayerInfoMap.size());
226}
227
228PCachePlayerInfo ObjectMgr::GetPlayerInfoFromCache(uint32 unPlayerGuid) const
229{
230    //Now m_mPlayerInfoMap is using only for search, but when dinamic inserting/removing
231    //will be implemented we should lock it to prevent simultaneous access.
232    //Inserting - when new created player is saving
233    //Removing - when player has been deleted
234    CachePlayerInfoMap::const_iterator ipos = m_mPlayerInfoMap.find(unPlayerGuid);
235    return ipos == m_mPlayerInfoMap.end() ? NULL : ipos->second;
236}
237
238Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const
239{
240    for(GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
241        if ((*itr)->GetLeaderGUID() == guid)
242            return *itr;
243
244    return NULL;
245}
246
247Guild * ObjectMgr::GetGuildById(const uint32 GuildId) const
248{
249    for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
250        if ((*itr)->GetId() == GuildId)
251            return *itr;
252
253    return NULL;
254}
255
256Guild * ObjectMgr::GetGuildByName(std::string guildname) const
257{
258    for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
259        if ((*itr)->GetName() == guildname)
260            return *itr;
261
262    return NULL;
263}
264
265std::string ObjectMgr::GetGuildNameById(const uint32 GuildId) const
266{
267    for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); itr++)
268        if ((*itr)->GetId() == GuildId)
269            return (*itr)->GetName();
270
271    return "";
272}
273
274Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
275{
276    for(GuildSet::const_iterator itr = mGuildSet.begin(); itr != mGuildSet.end(); ++itr)
277        if( (*itr)->GetLeader() == guid)
278            return *itr;
279
280    return NULL;
281}
282
283ArenaTeam* ObjectMgr::GetArenaTeamById(const uint32 ArenaTeamId) const
284{
285    for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
286        if ((*itr)->GetId() == ArenaTeamId)
287            return *itr;
288
289    return NULL;
290}
291
292ArenaTeam* ObjectMgr::GetArenaTeamByName(std::string arenateamname) const
293{
294    for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
295        if ((*itr)->GetName() == arenateamname)
296            return *itr;
297
298    return NULL;
299}
300
301ArenaTeam* ObjectMgr::GetArenaTeamByCapitan(uint64 const& guid) const
302{
303    for(ArenaTeamSet::const_iterator itr = mArenaTeamSet.begin(); itr != mArenaTeamSet.end(); itr++)
304        if ((*itr)->GetCaptain() == guid)
305            return *itr;
306
307    return NULL;
308}
309
310AuctionHouseObject * ObjectMgr::GetAuctionsMap( uint32 location )
311{
312    switch ( location )
313    {
314        case 6:                                             //horde
315            return & mHordeAuctions;
316            break;
317        case 2:                                             //alliance
318            return & mAllianceAuctions;
319            break;
320        default:                                            //neutral
321            return & mNeutralAuctions;
322    }
323}
324
325uint32 ObjectMgr::GetAuctionCut(uint32 location, uint32 highBid)
326{
327    if (location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
328        return (uint32) (0.15f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
329    else
330        return (uint32) (0.05f * highBid * sWorld.getRate(RATE_AUCTION_CUT));
331}
332
333uint32 ObjectMgr::GetAuctionDeposit(uint32 location, uint32 time, Item *pItem)
334{
335    float percentance;                                      // in 0..1
336    if ( location == 7 && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
337        percentance = 0.75f;
338    else
339        percentance = 0.15f;
340
341    percentance *= sWorld.getRate(RATE_AUCTION_DEPOSIT);
342
343    return uint32( percentance * pItem->GetProto()->SellPrice * pItem->GetCount() * (time / MIN_AUCTION_TIME ) );
344}
345
346/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
347uint32 ObjectMgr::GetAuctionOutBid(uint32 currentBid)
348{
349    uint32 outbid = (currentBid / 100) * 5;
350    if (!outbid)
351        outbid = 1;
352    return outbid;
353}
354
355//does not clear ram
356void ObjectMgr::SendAuctionWonMail( AuctionEntry *auction )
357{
358    Item *pItem = GetAItem(auction->item_guidlow);
359    if(!pItem)
360        return;
361
362    uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
363    Player *bidder = GetPlayer(bidder_guid);
364
365    uint32 bidder_accId = 0;
366
367    // data for gm.log
368    if( sWorld.getConfig(CONFIG_GM_LOG_TRADE) )
369    {
370        uint32 bidder_security = 0;
371        std::string bidder_name;
372        if (bidder)
373        {
374            bidder_accId = bidder->GetSession()->GetAccountId();
375            bidder_security = bidder->GetSession()->GetSecurity();
376            bidder_name = bidder->GetName();
377        }
378        else
379        {
380            bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
381            bidder_security = accmgr.GetSecurity(bidder_accId);
382
383            if(bidder_security > SEC_PLAYER )               // not do redundant DB requests
384            {
385                if(!GetPlayerNameByGUID(bidder_guid,bidder_name))
386                    bidder_name = GetTrinityStringForDBCLocale(LANG_UNKNOWN);
387            }
388        }
389
390        if( bidder_security > SEC_PLAYER )
391        {
392            std::string owner_name;
393            if(!GetPlayerNameByGUID(auction->owner,owner_name))
394                owner_name = GetTrinityStringForDBCLocale(LANG_UNKNOWN);
395
396            uint32 owner_accid = GetPlayerAccountIdByGUID(auction->owner);
397
398            sLog.outCommand("GM %s (Account: %u) won item in auction: %s (Entry: %u Count: %u) and pay money: %u. Original owner %s (Account: %u)",
399                bidder_name.c_str(),bidder_accId,pItem->GetProto()->Name1,pItem->GetEntry(),pItem->GetCount(),auction->bid,owner_name.c_str(),owner_accid);
400        }
401    }
402    else if(!bidder)
403        bidder_accId = GetPlayerAccountIdByGUID(bidder_guid);
404
405    // receiver exist
406    if(bidder || bidder_accId)
407    {
408        std::ostringstream msgAuctionWonSubject;
409        msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON;
410
411        std::ostringstream msgAuctionWonBody;
412        msgAuctionWonBody.width(16);
413        msgAuctionWonBody << std::right << std::hex << auction->owner;
414        msgAuctionWonBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
415        sLog.outDebug( "AuctionWon body string : %s", msgAuctionWonBody.str().c_str() );
416
417        //prepare mail data... :
418        uint32 itemTextId = this->CreateItemText( msgAuctionWonBody.str() );
419
420        // set owner to bidder (to prevent delete item with sender char deleting)
421        // owner in `data` will set at mail receive and item extracting
422        CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'",auction->bidder,pItem->GetGUIDLow());
423        CharacterDatabase.CommitTransaction();
424
425        MailItemsInfo mi;
426        mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
427
428        if (bidder)
429            bidder->GetSession()->SendAuctionBidderNotification( auction->location, auction->Id, bidder_guid, 0, 0, auction->item_template);
430        else
431            RemoveAItem(pItem->GetGUIDLow());               // we have to remove the item, before we delete it !!
432
433        // will delete item or place to receiver mail list
434        WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionWonSubject.str(), itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_AUCTION);
435    }
436    // receiver not exist
437    else
438    {
439        CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", pItem->GetGUIDLow());
440        RemoveAItem(pItem->GetGUIDLow());                   // we have to remove the item, before we delete it !!
441        delete pItem;
442    }
443}
444
445void ObjectMgr::SendAuctionSalePendingMail( AuctionEntry * auction )
446{
447    uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
448    Player *owner = GetPlayer(owner_guid);
449
450    // owner exist (online or offline)
451    if(owner || GetPlayerAccountIdByGUID(owner_guid))
452    {
453        std::ostringstream msgAuctionSalePendingSubject;
454        msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING;
455
456        std::ostringstream msgAuctionSalePendingBody;
457        uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
458
459        time_t distrTime = time(NULL) + HOUR;
460
461        msgAuctionSalePendingBody.width(16);
462        msgAuctionSalePendingBody << std::right << std::hex << auction->bidder;
463        msgAuctionSalePendingBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
464        msgAuctionSalePendingBody << ":" << auction->deposit << ":" << auctionCut << ":0:";
465        msgAuctionSalePendingBody << secsToTimeBitFields(distrTime);
466
467        sLog.outDebug("AuctionSalePending body string : %s", msgAuctionSalePendingBody.str().c_str());
468
469        uint32 itemTextId = this->CreateItemText( msgAuctionSalePendingBody.str() );
470
471        WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSalePendingSubject.str(), itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_AUCTION);
472    }
473}
474
475//call this method to send mail to auction owner, when auction is successful, it does not clear ram
476void ObjectMgr::SendAuctionSuccessfulMail( AuctionEntry * auction )
477{
478    uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
479    Player *owner = GetPlayer(owner_guid);
480
481    uint32 owner_accId = 0;
482    if(!owner)
483        owner_accId = GetPlayerAccountIdByGUID(owner_guid);
484
485    // owner exist
486    if(owner || owner_accId)
487    {
488        std::ostringstream msgAuctionSuccessfulSubject;
489        msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL;
490
491        std::ostringstream auctionSuccessfulBody;
492        uint32 auctionCut = GetAuctionCut(auction->location, auction->bid);
493
494        auctionSuccessfulBody.width(16);
495        auctionSuccessfulBody << std::right << std::hex << auction->bidder;
496        auctionSuccessfulBody << std::dec << ":" << auction->bid << ":" << auction->buyout;
497        auctionSuccessfulBody << ":" << auction->deposit << ":" << auctionCut;
498
499        sLog.outDebug("AuctionSuccessful body string : %s", auctionSuccessfulBody.str().c_str());
500
501        uint32 itemTextId = this->CreateItemText( auctionSuccessfulBody.str() );
502
503        uint32 profit = auction->bid + auction->deposit - auctionCut;
504
505        if (owner)
506        {
507            //send auction owner notification, bidder must be current!
508            owner->GetSession()->SendAuctionOwnerNotification( auction );
509        }
510
511        WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->owner, msgAuctionSuccessfulSubject.str(), itemTextId, NULL, profit, 0, MAIL_CHECK_MASK_AUCTION, HOUR);
512    }
513}
514
515//does not clear ram
516void ObjectMgr::SendAuctionExpiredMail( AuctionEntry * auction )
517{                                                           //return an item in auction to its owner by mail
518    Item *pItem = GetAItem(auction->item_guidlow);
519    if(!pItem)
520    {
521        sLog.outError("Auction item (GUID: %u) not found, and lost.",auction->item_guidlow);
522        return;
523    }
524
525    uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
526    Player *owner = GetPlayer(owner_guid);
527
528    uint32 owner_accId = 0;
529    if(!owner)
530        owner_accId = GetPlayerAccountIdByGUID(owner_guid);
531
532    // owner exist
533    if(owner || owner_accId)
534    {
535        std::ostringstream subject;
536        subject << auction->item_template << ":0:" << AUCTION_EXPIRED;
537
538        if ( owner )
539            owner->GetSession()->SendAuctionOwnerNotification( auction );
540        else
541            RemoveAItem(pItem->GetGUIDLow());               // we have to remove the item, before we delete it !!
542
543        MailItemsInfo mi;
544        mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
545
546        // will delete item or place to receiver mail list
547        WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, GUID_LOPART(owner_guid), subject.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
548
549    }
550    // owner not found
551    else
552    {
553        CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'",pItem->GetGUIDLow());
554        RemoveAItem(pItem->GetGUIDLow());                   // we have to remove the item, before we delete it !!
555        delete pItem;
556    }
557}
558
559CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
560{
561    return sCreatureStorage.LookupEntry<CreatureInfo>(id);
562}
563
564void ObjectMgr::LoadCreatureLocales()
565{
566    mCreatureLocaleMap.clear();
567   
568    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");
569
570    if(!result)
571    {
572        barGoLink bar(1);
573
574        bar.step();
575
576        sLog.outString("");
577        sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
578        return;
579    }
580
581    barGoLink bar(result->GetRowCount());
582
583    do
584    {
585        Field *fields = result->Fetch();
586        bar.step();
587
588        uint32 entry = fields[0].GetUInt32();
589
590        CreatureLocale& data = mCreatureLocaleMap[entry];
591
592        for(int i = 1; i < MAX_LOCALE; ++i)
593        {
594            std::string str = fields[1+2*(i-1)].GetCppString();
595            if(!str.empty())
596            {
597                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
598                if(idx >= 0)
599                {
600                    if(data.Name.size() <= idx)
601                        data.Name.resize(idx+1);
602
603                    data.Name[idx] = str;
604                }
605            }
606            str = fields[1+2*(i-1)+1].GetCppString();
607            if(!str.empty())
608            {
609                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
610                if(idx >= 0)
611                {
612                    if(data.SubName.size() <= idx)
613                        data.SubName.resize(idx+1);
614
615                    data.SubName[idx] = str;
616                }
617            }
618        }
619    } while (result->NextRow());
620
621    delete result;
622
623    sLog.outString();
624    sLog.outString( ">> Loaded %u creature locale strings", mCreatureLocaleMap.size() );
625}
626
627void ObjectMgr::LoadCreatureTemplates()
628{
629    sCreatureStorage.Load();
630
631    sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
632    sLog.outString();
633
634    std::set<uint32> heroicEntries;                         // already loaded heroic value in creatures
635    std::set<uint32> hasHeroicEntries;                      // already loaded creatures with heroic entry values
636
637    // check data correctness
638    for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
639    {
640        CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
641        if(!cInfo)
642            continue;
643
644        if(cInfo->HeroicEntry)
645        {
646            CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
647            if(!heroicInfo)
648            {
649                sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.",cInfo->HeroicEntry,cInfo->HeroicEntry);
650                continue;
651            }
652
653            if(heroicEntries.find(i)!=heroicEntries.end())
654            {
655                sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i);
656                continue;
657            }
658
659            if(heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end())
660            {
661                sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry);
662                continue;
663            }
664
665            if(hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end())
666            {
667                sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry);
668                continue;
669            }
670
671            if(cInfo->npcflag != heroicInfo->npcflag)
672            {
673                sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `npcflag` in heroic mode.",i);
674                continue;
675            }
676
677            if(cInfo->classNum != heroicInfo->classNum)
678            {
679                sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `classNum` in heroic mode.",i);
680                continue;
681            }
682
683            if(cInfo->race != heroicInfo->race)
684            {
685                sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `race` in heroic mode.",i);
686                continue;
687            }
688
689            if(cInfo->trainer_type != heroicInfo->trainer_type)
690            {
691                sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_type` in heroic mode.",i);
692                continue;
693            }
694
695            if(cInfo->trainer_spell != heroicInfo->trainer_spell)
696            {
697                sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_spell` in heroic mode.",i);
698                continue;
699            }
700
701            hasHeroicEntries.insert(i);
702            heroicEntries.insert(cInfo->HeroicEntry);
703        }
704
705        FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
706        if(!factionTemplate)
707            sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_A template (%u)", cInfo->Entry, cInfo->faction_A);
708
709        factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
710        if(!factionTemplate)
711            sLog.outErrorDb("Creature (Entry: %u) has non-existing faction_H template (%u)", cInfo->Entry, cInfo->faction_H);
712
713        CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_A);
714        if (!minfo)
715            sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_A (%u)", cInfo->Entry, cInfo->DisplayID_A);
716        minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->DisplayID_H);
717        if (!minfo)
718            sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_H (%u)", cInfo->Entry, cInfo->DisplayID_H);
719
720        if(cInfo->dmgschool >= MAX_SPELL_SCHOOL)
721        {
722            sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool);
723            const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
724        }
725
726        if(cInfo->baseattacktime == 0)
727            const_cast<CreatureInfo*>(cInfo)->baseattacktime  = BASE_ATTACK_TIME;
728
729        if(cInfo->rangeattacktime == 0)
730            const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
731
732        if((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
733            sLog.outErrorDb("Creature (Entry: %u) has wrong trainer type %u",cInfo->Entry,cInfo->trainer_type);
734
735        if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
736        {
737            sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType);
738            const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
739        }
740
741        if(cInfo->PetSpellDataId)
742        {
743            CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
744            if(!spellDataId)
745                sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
746        }
747
748        if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
749        {
750            sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
751            const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
752        }
753
754        if(cInfo->equipmentId > 0)                          // 0 no equipment
755        {
756            if(!GetEquipmentInfo(cInfo->equipmentId))
757            {
758                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);
759                const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
760            }
761        }
762
763        /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
764        if(cInfo->scale <= 0.0f)
765        {
766            CreatureDisplayInfoEntry const* ScaleEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A);
767            const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f;
768        }
769    }
770}
771
772void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
773{
774    // Now add the auras, format "spellid effectindex spellid effectindex..."
775    char *p,*s;
776    std::vector<int> val;
777    s=p=(char*)reinterpret_cast<char const*>(addon->auras);
778    if(p)
779    {
780        while (p[0]!=0)
781        {
782            ++p;
783            if (p[0]==' ')
784            {
785                val.push_back(atoi(s));
786                s=++p;
787            }
788        }
789        if (p!=s)
790            val.push_back(atoi(s));
791
792        // free char* loaded memory
793        delete[] (char*)reinterpret_cast<char const*>(addon->auras);
794
795        // wrong list
796        if (val.size()%2)
797        {
798            addon->auras = NULL;
799            sLog.outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`.",guidEntryStr,addon->guidOrEntry,table);
800            return;
801        }
802    }
803
804    // empty list
805    if(val.empty())
806    {
807        addon->auras = NULL;
808        return;
809    }
810
811    // replace by new strucutres array
812    const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
813
814    int i=0;
815    for(int j=0;j<val.size()/2;++j)
816    {
817        CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
818        cAura.spell_id = (uint32)val[2*j+0];
819        cAura.effect_idx  = (uint32)val[2*j+1];
820        if ( cAura.effect_idx > 2 )
821        {
822            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);
823            continue;
824        }
825        SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
826        if (!AdditionalSpellInfo)
827        {
828            sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.spell_id,table);
829            continue;
830        }
831
832        if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
833        {
834            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);
835            continue;
836        }
837
838        ++i;
839    }
840
841    // fill terminator element (after last added)
842    CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
843    endAura.spell_id   = 0;
844    endAura.effect_idx = 0;
845}
846
847void ObjectMgr::LoadCreatureAddons()
848{
849    sCreatureInfoAddonStorage.Load();
850
851    sLog.outString( ">> Loaded %u creature template addons", sCreatureInfoAddonStorage.RecordCount );
852    sLog.outString();
853
854    // check data correctness and convert 'auras'
855    for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
856    {
857        CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i);
858        if(!addon)
859            continue;
860
861        ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_template_addon", "Entry");
862
863        if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
864            sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `creature_template_addon`",addon->guidOrEntry);
865    }
866
867    sCreatureDataAddonStorage.Load();
868
869    sLog.outString( ">> Loaded %u creature addons", sCreatureDataAddonStorage.RecordCount );
870    sLog.outString();
871
872    // check data correctness and convert 'auras'
873    for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
874    {
875        CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i);
876        if(!addon)
877            continue;
878
879        ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), "creature_addon", "GUIDLow");
880
881        if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end())
882            sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry);
883    }
884}
885
886EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
887{
888    return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
889}
890
891void ObjectMgr::LoadEquipmentTemplates()
892{
893    sEquipmentStorage.Load();
894
895    sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
896    sLog.outString();
897}
898
899CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
900{
901    return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
902}
903
904uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data)
905{
906    // Load creature model (display id)
907    uint32 display_id;
908    if (!data || data->displayid == 0)                      // use defaults from the template
909    {
910        // DisplayID_A is used if no team is given
911        if (team == HORDE)
912            display_id = (cinfo->DisplayID_H2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_H2 : cinfo->DisplayID_H;
913        else
914            display_id = (cinfo->DisplayID_A2 != 0 && urand(0,1) == 0) ? cinfo->DisplayID_A2 : cinfo->DisplayID_A;
915    }
916    else                                                    // overriden in creature data
917        display_id = data->displayid;
918
919    return display_id;
920}
921
922CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_id)
923{
924    CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
925    if(!minfo)
926        return NULL;
927
928    // If a model for another gender exists, 50% chance to use it
929    if(minfo->modelid_other_gender != 0 && urand(0,1) == 0)
930    {
931        CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
932        if(!minfo_tmp)
933        {
934            sLog.outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
935            return minfo;                                   // not fatal, just use the previous one
936        }
937        else
938            return minfo_tmp;
939    }
940    else
941        return minfo;
942}
943
944void ObjectMgr::LoadCreatureModelInfo()
945{
946    sCreatureModelStorage.Load();
947
948    sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount );
949    sLog.outString();
950}
951
952void ObjectMgr::LoadCreatures()
953{
954    uint32 count = 0;
955    //                                                0              1   2    3
956    QueryResult *result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid,"
957    //   4             5           6           7           8            9              10         11
958        "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint,"
959    //   12         13       14          15            16         17
960        "curhealth, curmana, DeathState, MovementType, spawnMask, event "
961        "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid");
962
963    if(!result)
964    {
965        barGoLink bar(1);
966
967        bar.step();
968
969        sLog.outString("");
970        sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
971        return;
972    }
973
974    // build single time for check creature data
975    std::set<uint32> heroicCreatures;
976    for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
977        if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
978            if(cInfo->HeroicEntry)
979                heroicCreatures.insert(cInfo->HeroicEntry);
980
981    barGoLink bar(result->GetRowCount());
982
983    do
984    {
985        Field *fields = result->Fetch();
986        bar.step();
987
988        uint32 guid = fields[0].GetUInt32();
989
990        CreatureData& data = mCreatureDataMap[guid];
991
992        data.id             = fields[ 1].GetUInt32();
993        data.mapid          = fields[ 2].GetUInt32();
994        data.displayid      = fields[ 3].GetUInt32();
995        data.equipmentId    = fields[ 4].GetUInt32();
996        data.posX           = fields[ 5].GetFloat();
997        data.posY           = fields[ 6].GetFloat();
998        data.posZ           = fields[ 7].GetFloat();
999        data.orientation    = fields[ 8].GetFloat();
1000        data.spawntimesecs  = fields[ 9].GetUInt32();
1001        data.spawndist      = fields[10].GetFloat();
1002        data.currentwaypoint= fields[11].GetUInt32();
1003        data.curhealth      = fields[12].GetUInt32();
1004        data.curmana        = fields[13].GetUInt32();
1005        data.is_dead        = fields[14].GetBool();
1006        data.movementType   = fields[15].GetUInt8();
1007        data.spawnMask      = fields[16].GetUInt8();
1008        int16 gameEvent     = fields[17].GetInt16();
1009
1010        CreatureInfo const* cInfo = GetCreatureTemplate(data.id);
1011        if(!cInfo)
1012        {
1013            sLog.outErrorDb("Table `creature` have creature (GUID: %u) with not existed creature entry %u, skipped.",guid,data.id );
1014            continue;
1015        }
1016
1017        if(heroicCreatures.find(data.id)!=heroicCreatures.end())
1018        {
1019            sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id );
1020            continue;
1021        }
1022
1023        if(data.equipmentId > 0)                            // -1 no equipment, 0 use default
1024        {
1025            if(!GetEquipmentInfo(data.equipmentId))
1026            {
1027                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);
1028                data.equipmentId = -1;
1029            }
1030        }
1031
1032        if(cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
1033        {
1034            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 );
1035            data.curhealth = cInfo->minhealth;
1036        }
1037
1038        if(data.curmana < cInfo->minmana)
1039        {
1040            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 );
1041            data.curmana = cInfo->minmana;
1042        }
1043
1044        if(data.spawndist < 0.0f)
1045        {
1046            sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.",guid,data.id );
1047            data.spawndist = 0.0f;
1048        }
1049        else if(data.movementType == RANDOM_MOTION_TYPE)
1050        {
1051            if(data.spawndist == 0.0f)
1052            {
1053                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 );
1054                data.movementType = IDLE_MOTION_TYPE;
1055            }
1056        }
1057        else if(data.movementType == IDLE_MOTION_TYPE)
1058        {
1059            if(data.spawndist != 0.0f)
1060            {
1061                sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.",guid,data.id );
1062                data.spawndist = 0.0f;
1063            }
1064        }
1065
1066        if (gameEvent==0)                                   // if not this is to be managed by GameEvent System
1067            AddCreatureToGrid(guid, &data);
1068        ++count;
1069
1070    } while (result->NextRow());
1071
1072    delete result;
1073
1074    sLog.outString();
1075    sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() );
1076}
1077
1078void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
1079{
1080    uint8 mask = data->spawnMask;
1081    for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1082    {
1083        if(mask & 1)
1084        {
1085            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
1086            uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1087
1088            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1089            cell_guids.creatures.insert(guid);
1090        }
1091    }
1092}
1093
1094void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
1095{
1096    uint8 mask = data->spawnMask;
1097    for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1098    {
1099        if(mask & 1)
1100        {
1101            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
1102            uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1103
1104            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1105            cell_guids.creatures.erase(guid);
1106        }
1107    }
1108}
1109
1110void ObjectMgr::LoadGameobjects()
1111{
1112    uint32 count = 0;
1113
1114    //                                                0                1   2    3           4           5           6
1115    QueryResult *result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation,"
1116    //   7          8          9          10         11             12            13     14         15
1117        "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, event "
1118        "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid");
1119
1120    if(!result)
1121    {
1122        barGoLink bar(1);
1123
1124        bar.step();
1125
1126        sLog.outString();
1127        sLog.outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
1128        return;
1129    }
1130
1131    barGoLink bar(result->GetRowCount());
1132
1133    do
1134    {
1135        Field *fields = result->Fetch();
1136        bar.step();
1137
1138        uint32 guid = fields[0].GetUInt32();
1139
1140        GameObjectData& data = mGameObjectDataMap[guid];
1141
1142        data.id             = fields[ 1].GetUInt32();
1143        data.mapid          = fields[ 2].GetUInt32();
1144        data.posX           = fields[ 3].GetFloat();
1145        data.posY           = fields[ 4].GetFloat();
1146        data.posZ           = fields[ 5].GetFloat();
1147        data.orientation    = fields[ 6].GetFloat();
1148        data.rotation0      = fields[ 7].GetFloat();
1149        data.rotation1      = fields[ 8].GetFloat();
1150        data.rotation2      = fields[ 9].GetFloat();
1151        data.rotation3      = fields[10].GetFloat();
1152        data.spawntimesecs  = fields[11].GetInt32();
1153        data.animprogress   = fields[12].GetUInt32();
1154        data.go_state       = fields[13].GetUInt32();
1155        data.ArtKit         = 0;
1156        data.spawnMask      = fields[14].GetUInt8();
1157        int16 gameEvent     = fields[15].GetInt16();
1158
1159        GameObjectInfo const* gInfo = GetGameObjectInfo(data.id);
1160        if(!gInfo)
1161        {
1162            sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u) with not existed gameobject entry %u, skipped.",guid,data.id );
1163            continue;
1164        }
1165
1166        if (gameEvent==0)                                   // if not this is to be managed by GameEvent System
1167            AddGameobjectToGrid(guid, &data);
1168        ++count;
1169
1170    } while (result->NextRow());
1171
1172    delete result;
1173
1174    sLog.outString();
1175    sLog.outString( ">> Loaded %u gameobjects", mGameObjectDataMap.size());
1176}
1177
1178void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
1179{
1180    uint8 mask = data->spawnMask;
1181    for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1182    {
1183        if(mask & 1)
1184        {
1185            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
1186            uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1187
1188            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1189            cell_guids.gameobjects.insert(guid);
1190        }
1191    }
1192}
1193
1194void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data)
1195{
1196    uint8 mask = data->spawnMask;
1197    for(uint8 i = 0; mask != 0; i++, mask >>= 1)
1198    {
1199        if(mask & 1)
1200        {
1201            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
1202            uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;
1203
1204            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid,i)][cell_id];
1205            cell_guids.gameobjects.erase(guid);
1206        }
1207    }
1208}
1209
1210void ObjectMgr::LoadCreatureRespawnTimes()
1211{
1212    // remove outdated data
1213    WorldDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
1214
1215    uint32 count = 0;
1216
1217    QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM creature_respawn");
1218
1219    if(!result)
1220    {
1221        barGoLink bar(1);
1222
1223        bar.step();
1224
1225        sLog.outString();
1226        sLog.outString(">> Loaded 0 creature respawn time.");
1227        return;
1228    }
1229
1230    barGoLink bar(result->GetRowCount());
1231
1232    do
1233    {
1234        Field *fields = result->Fetch();
1235        bar.step();
1236
1237        uint32 loguid       = fields[0].GetUInt32();
1238        uint64 respawn_time = fields[1].GetUInt64();
1239        uint32 instance     = fields[2].GetUInt32();
1240
1241        mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
1242
1243        ++count;
1244    } while (result->NextRow());
1245
1246    delete result;
1247
1248    sLog.outString( ">> Loaded %u creature respawn times", mCreatureRespawnTimes.size() );
1249    sLog.outString();
1250}
1251
1252void ObjectMgr::LoadGameobjectRespawnTimes()
1253{
1254    // remove outdated data
1255    WorldDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
1256
1257    uint32 count = 0;
1258
1259    QueryResult *result = WorldDatabase.Query("SELECT guid,respawntime,instance FROM gameobject_respawn");
1260
1261    if(!result)
1262    {
1263        barGoLink bar(1);
1264
1265        bar.step();
1266
1267        sLog.outString();
1268        sLog.outString(">> Loaded 0 gameobject respawn time.");
1269        return;
1270    }
1271
1272    barGoLink bar(result->GetRowCount());
1273
1274    do
1275    {
1276        Field *fields = result->Fetch();
1277        bar.step();
1278
1279        uint32 loguid       = fields[0].GetUInt32();
1280        uint64 respawn_time = fields[1].GetUInt64();
1281        uint32 instance     = fields[2].GetUInt32();
1282
1283        mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = time_t(respawn_time);
1284
1285        ++count;
1286    } while (result->NextRow());
1287
1288    delete result;
1289
1290    sLog.outString( ">> Loaded %u gameobject respawn times", mGORespawnTimes.size() );
1291    sLog.outString();
1292}
1293
1294// name must be checked to correctness (if received) before call this function
1295uint64 ObjectMgr::GetPlayerGUIDByName(std::string name) const
1296{
1297    uint64 guid = 0;
1298
1299    CharacterDatabase.escape_string(name);
1300
1301    // Player name safe to sending to DB (checked at login) and this function using
1302    QueryResult *result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
1303    if(result)
1304    {
1305        guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
1306
1307        delete result;
1308    }
1309
1310    return guid;
1311}
1312
1313bool ObjectMgr::GetPlayerNameByGUID(const uint64 &guid, std::string &name) const
1314{
1315    // prevent DB access for online player
1316    if(Player* player = GetPlayer(guid))
1317    {
1318        name = player->GetName();
1319        return true;
1320    }
1321
1322    PCachePlayerInfo pInfo = GetPlayerInfoFromCache(GUID_LOPART(guid));
1323    if(pInfo)
1324    {
1325        name = pInfo->sPlayerName.c_str();
1326        return true;
1327    }
1328
1329    QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1330
1331    if(result)
1332    {
1333        name = (*result)[0].GetCppString();
1334        delete result;
1335        return true;
1336    }
1337
1338    return false;
1339}
1340
1341uint32 ObjectMgr::GetPlayerTeamByGUID(const uint64 &guid) const
1342{
1343    QueryResult *result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1344
1345    if(result)
1346    {
1347        uint8 race = (*result)[0].GetUInt8();
1348        delete result;
1349        return Player::TeamForRace(race);
1350    }
1351
1352    return 0;
1353}
1354
1355uint32 ObjectMgr::GetPlayerAccountIdByGUID(const uint64 &guid) const
1356{
1357    QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
1358    if(result)
1359    {
1360        uint32 acc = (*result)[0].GetUInt32();
1361        delete result;
1362        return acc;
1363    }
1364
1365    return 0;
1366}
1367
1368void ObjectMgr::LoadAuctions()
1369{
1370    QueryResult *result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse");
1371    if( !result )
1372        return;
1373
1374    Field *fields = result->Fetch();
1375    uint32 AuctionCount=fields[0].GetUInt32();
1376    delete result;
1377
1378    if(!AuctionCount)
1379        return;
1380
1381    result = CharacterDatabase.Query( "SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location FROM auctionhouse" );
1382    if( !result )
1383        return;
1384
1385    barGoLink bar( AuctionCount );
1386
1387    AuctionEntry *aItem;
1388
1389    do
1390    {
1391        fields = result->Fetch();
1392
1393        bar.step();
1394
1395        aItem = new AuctionEntry;
1396        aItem->Id = fields[0].GetUInt32();
1397        aItem->auctioneer = fields[1].GetUInt32();
1398        aItem->item_guidlow = fields[2].GetUInt32();
1399        aItem->item_template = fields[3].GetUInt32();
1400        aItem->owner = fields[4].GetUInt32();
1401        aItem->buyout = fields[5].GetUInt32();
1402        aItem->time = fields[6].GetUInt32();
1403        aItem->bidder = fields[7].GetUInt32();
1404        aItem->bid = fields[8].GetUInt32();
1405        aItem->startbid = fields[9].GetUInt32();
1406        aItem->deposit = fields[10].GetUInt32();
1407        aItem->location = fields[11].GetUInt8();
1408        //check if sold item exists
1409        if ( this->GetAItem( aItem->item_guidlow ) )
1410        {
1411            GetAuctionsMap( aItem->location )->AddAuction(aItem);
1412        }
1413        else
1414        {
1415            CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",aItem->Id);
1416            sLog.outError("Auction %u has not a existing item : %u", aItem->Id, aItem->item_guidlow);
1417            delete aItem;
1418        }
1419    } while (result->NextRow());
1420    delete result;
1421
1422    sLog.outString();
1423    sLog.outString( ">> Loaded %u auctions", AuctionCount );
1424    sLog.outString();
1425}
1426
1427void ObjectMgr::LoadItemLocales()
1428{
1429    mItemLocaleMap.clear();
1430   
1431    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");
1432
1433    if(!result)
1434    {
1435        barGoLink bar(1);
1436
1437        bar.step();
1438
1439        sLog.outString("");
1440        sLog.outString(">> Loaded 0 Item locale strings. DB table `locales_item` is empty.");
1441        return;
1442    }
1443
1444    barGoLink bar(result->GetRowCount());
1445
1446    do
1447    {
1448        Field *fields = result->Fetch();
1449        bar.step();
1450
1451        uint32 entry = fields[0].GetUInt32();
1452
1453        ItemLocale& data = mItemLocaleMap[entry];
1454
1455        for(int i = 1; i < MAX_LOCALE; ++i)
1456        {
1457            std::string str = fields[1+2*(i-1)].GetCppString();
1458            if(!str.empty())
1459            {
1460                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1461                if(idx >= 0)
1462                {
1463                    if(data.Name.size() <= idx)
1464                        data.Name.resize(idx+1);
1465
1466                    data.Name[idx] = str;
1467                }
1468            }
1469
1470            str = fields[1+2*(i-1)+1].GetCppString();
1471            if(!str.empty())
1472            {
1473                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
1474                if(idx >= 0)
1475                {
1476                    if(data.Description.size() <= idx)
1477                        data.Description.resize(idx+1);
1478
1479                    data.Description[idx] = str;
1480                }
1481            }
1482        }
1483    } while (result->NextRow());
1484
1485    delete result;
1486
1487    sLog.outString();
1488    sLog.outString( ">> Loaded %u Item locale strings", mItemLocaleMap.size() );
1489}
1490
1491void ObjectMgr::LoadItemPrototypes()
1492{
1493    sItemStorage.Load ();
1494    sLog.outString( ">> Loaded %u item prototypes", sItemStorage.RecordCount );
1495    sLog.outString();
1496
1497    // check data correctness
1498    for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
1499    {
1500        ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype >(i);
1501        ItemEntry const *dbcitem = sItemStore.LookupEntry(i);
1502        if(!proto)
1503        {
1504            /* to many errors, and possible not all items really used in game
1505            if (dbcitem)
1506                sLog.outErrorDb("Item (Entry: %u) doesn't exists in DB, but must exist.",i);
1507            */
1508            continue;
1509        }
1510
1511        if(dbcitem)
1512        {
1513            if(proto->InventoryType != dbcitem->InventoryType)
1514            {
1515                sLog.outErrorDb("Item (Entry: %u) not correct %u inventory type, must be %u (still using DB value).",i,proto->InventoryType,dbcitem->InventoryType);
1516                // It safe let use InventoryType from DB
1517            }
1518
1519            if(proto->DisplayInfoID != dbcitem->DisplayId)
1520            {
1521                sLog.outErrorDb("Item (Entry: %u) not correct %u display id, must be %u (using it).",i,proto->DisplayInfoID,dbcitem->DisplayId);
1522                const_cast<ItemPrototype*>(proto)->DisplayInfoID = dbcitem->DisplayId;
1523            }
1524            if(proto->Sheath != dbcitem->Sheath)
1525            {
1526                sLog.outErrorDb("Item (Entry: %u) not correct %u sheath, must be %u  (using it).",i,proto->Sheath,dbcitem->Sheath);
1527                const_cast<ItemPrototype*>(proto)->Sheath = dbcitem->Sheath;
1528            }
1529        }
1530        else
1531        {
1532            sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existed items).",i);
1533        }
1534
1535        if(proto->Class >= MAX_ITEM_CLASS)
1536        {
1537            sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class);
1538            const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_JUNK;
1539        }
1540
1541        if(proto->SubClass >= MaxItemSubclassValues[proto->Class])
1542        {
1543            sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u",i,proto->SubClass,proto->Class);
1544            const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
1545        }
1546
1547        if(proto->Quality >= MAX_ITEM_QUALITY)
1548        {
1549            sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)",i,proto->Quality);
1550            const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
1551        }
1552
1553        if(proto->BuyCount <= 0)
1554        {
1555            sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).",i,proto->BuyCount);
1556            const_cast<ItemPrototype*>(proto)->BuyCount = 1;
1557        }
1558
1559        if(proto->InventoryType >= MAX_INVTYPE)
1560        {
1561            sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType);
1562            const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
1563        }
1564
1565        if(proto->RequiredSkill >= MAX_SKILL_TYPE)
1566        {
1567            sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
1568            const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
1569        }
1570
1571        if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
1572        {
1573            sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped.",i,proto->AllowableClass);
1574        }
1575
1576        if(!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
1577        {
1578            sLog.outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped.",i,proto->AllowableRace);
1579        }
1580
1581        if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
1582        {
1583            sLog.outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)",i,proto->RequiredSpell);
1584            const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
1585        }
1586
1587        if(proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
1588            sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.",i,proto->RequiredReputationRank);
1589
1590        if(proto->RequiredReputationFaction)
1591        {
1592            if(!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
1593            {
1594                sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)",i,proto->RequiredReputationFaction);
1595                const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
1596            }
1597
1598            if(proto->RequiredReputationRank == MIN_REPUTATION_RANK)
1599                sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.",i);
1600        }
1601        else if(proto->RequiredReputationRank > MIN_REPUTATION_RANK)
1602            sLog.outErrorDb("Item (Entry: %u) has RequiredReputationFaction ==0 but RequiredReputationRank > 0, rank setting is useless.",i);
1603
1604        if(proto->Stackable==0)
1605        {
1606            sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%u), replace by default 1.",i,proto->Stackable);
1607            const_cast<ItemPrototype*>(proto)->Stackable = 1;
1608        }
1609        else if(proto->Stackable > 255)
1610        {
1611            sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (255).",i,proto->Stackable);
1612            const_cast<ItemPrototype*>(proto)->Stackable = 255;
1613        }
1614
1615        for (int j = 0; j < 10; j++)
1616        {
1617            // for ItemStatValue != 0
1618            if(proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
1619            {
1620                sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
1621                const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
1622            }
1623        }
1624
1625        for (int j = 0; j < 5; j++)
1626        {
1627            if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
1628            {
1629                sLog.outErrorDb("Item (Entry: %u) has wrong dmg_type%d (%u)",i,j+1,proto->Damage[j].DamageType);
1630                const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
1631            }
1632        }
1633
1634        // special format
1635        if(proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN)
1636        {
1637            // spell_1
1638            if(proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
1639            {
1640                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);
1641                const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1642                const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1643                const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1644                const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1645            }
1646
1647            // spell_2 have learning spell
1648            if(proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
1649            {
1650                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);
1651                const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1652                const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1653                const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1654            }
1655            else if(!proto->Spells[1].SpellId)
1656            {
1657                sLog.outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.",i,1+1);
1658                const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1659                const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1660            }
1661            else
1662            {
1663                SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
1664                if(!spellInfo)
1665                {
1666                    sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
1667                    const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1668                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1669                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1670                }
1671                // allowed only in special format
1672                else if(proto->Spells[1].SpellId==SPELL_ID_GENERIC_LEARN)
1673                {
1674                    sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,1+1,proto->Spells[1].SpellId);
1675                    const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
1676                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
1677                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1678                }
1679            }
1680
1681            // spell_3*,spell_4*,spell_5* is empty
1682            for (int j = 2; j < 5; j++)
1683            {
1684                if(proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
1685                {
1686                    sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
1687                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1688                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1689                }
1690                else if(proto->Spells[j].SpellId != 0)
1691                {
1692                    sLog.outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%u) for learning special format",i,j+1,proto->Spells[j].SpellId);
1693                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1694                }
1695            }
1696        }
1697        // normal spell list
1698        else
1699        {
1700            for (int j = 0; j < 5; j++)
1701            {
1702                if(proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
1703                {
1704                    sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)",i,j+1,proto->Spells[j].SpellTrigger);
1705                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1706                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
1707                }
1708
1709                if(proto->Spells[j].SpellId)
1710                {
1711                    SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
1712                    if(!spellInfo)
1713                    {
1714                        sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
1715                        const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1716                    }
1717                    // allowed only in special format
1718                    else if(proto->Spells[j].SpellId==SPELL_ID_GENERIC_LEARN)
1719                    {
1720                        sLog.outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%u)",i,j+1,proto->Spells[j].SpellId);
1721                        const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
1722                    }
1723                }
1724            }
1725        }
1726
1727        if(proto->Bonding >= MAX_BIND_TYPE)
1728            sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)",i,proto->Bonding);
1729
1730        if(proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
1731            sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i,proto->PageText);
1732
1733        if(proto->LockID && !sLockStore.LookupEntry(proto->LockID))
1734            sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)",i,proto->LockID);
1735
1736        if(proto->Sheath >= MAX_SHEATHETYPE)
1737        {
1738            sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)",i,proto->Sheath);
1739            const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
1740        }
1741
1742        if(proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
1743        {
1744            sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)",i,proto->RandomProperty);
1745            const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
1746        }
1747
1748        if(proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
1749        {
1750            sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)",i,proto->RandomSuffix);
1751            const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
1752        }
1753
1754        if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
1755        {
1756            sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet);
1757            const_cast<ItemPrototype*>(proto)->ItemSet = 0;
1758        }
1759
1760        if(proto->Area && !GetAreaEntryByAreaID(proto->Area))
1761            sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)",i,proto->Area);
1762
1763        if(proto->Map && !sMapStore.LookupEntry(proto->Map))
1764            sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map);
1765
1766        if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
1767            sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory);
1768
1769        for (int j = 0; j < 3; j++)
1770        {
1771            if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
1772            {
1773                sLog.outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)",i,j+1,proto->Socket[j].Color);
1774                const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
1775            }
1776        }
1777
1778        if(proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
1779            sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)",i,proto->GemProperties);
1780
1781        if(proto->FoodType >= MAX_PET_DIET)
1782        {
1783            sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType);
1784            const_cast<ItemPrototype*>(proto)->FoodType = 0;
1785        }
1786    }
1787
1788    // this DBC used currently only for check item templates in DB.
1789    sItemStore.Clear();
1790}
1791
1792void ObjectMgr::LoadAuctionItems()
1793{
1794    QueryResult *result = CharacterDatabase.Query( "SELECT itemguid,item_template FROM auctionhouse" );
1795
1796    if( !result )
1797        return;
1798
1799    barGoLink bar( result->GetRowCount() );
1800
1801    uint32 count = 0;
1802
1803    Field *fields;
1804    do
1805    {
1806        bar.step();
1807
1808        fields = result->Fetch();
1809        uint32 item_guid        = fields[0].GetUInt32();
1810        uint32 item_template    = fields[1].GetUInt32();
1811
1812        ItemPrototype const *proto = GetItemPrototype(item_template);
1813
1814        if(!proto)
1815        {
1816            sLog.outError( "ObjectMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template);
1817            continue;
1818        }
1819
1820        Item *item = NewItemOrBag(proto);
1821
1822        if(!item->LoadFromDB(item_guid,0))
1823        {
1824            delete item;
1825            continue;
1826        }
1827        AddAItem(item);
1828
1829        ++count;
1830    }
1831    while( result->NextRow() );
1832
1833    delete result;
1834
1835    sLog.outString();
1836    sLog.outString( ">> Loaded %u auction items", count );
1837}
1838
1839void ObjectMgr::LoadPetLevelInfo()
1840{
1841    // Loading levels data
1842    {
1843        //                                                 0               1      2   3     4    5    6    7     8    9
1844        QueryResult *result  = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
1845
1846        uint32 count = 0;
1847
1848        if (!result)
1849        {
1850            barGoLink bar( 1 );
1851
1852            sLog.outString();
1853            sLog.outString( ">> Loaded %u level pet stats definitions", count );
1854            sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table.");
1855            return;
1856        }
1857
1858        barGoLink bar( result->GetRowCount() );
1859
1860        do
1861        {
1862            Field* fields = result->Fetch();
1863
1864            uint32 creature_id = fields[0].GetUInt32();
1865            if(!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
1866            {
1867                sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.",creature_id);
1868                continue;
1869            }
1870
1871            uint32 current_level = fields[1].GetUInt32();
1872            if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
1873            {
1874                if(current_level > 255)                     // hardcoded level maximum
1875                    sLog.outErrorDb("Wrong (> 255) level %u in `pet_levelstats` table, ignoring.",current_level);
1876                else
1877                    sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `pet_levelstats` table, ignoring.",current_level);
1878                continue;
1879            }
1880            else if(current_level < 1)
1881            {
1882                sLog.outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.",current_level);
1883                continue;
1884            }
1885
1886            PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];
1887
1888            if(pInfoMapEntry==NULL)
1889                pInfoMapEntry =  new PetLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
1890
1891            // data for level 1 stored in [0] array element, ...
1892            PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level-1];
1893
1894            pLevelInfo->health = fields[2].GetUInt16();
1895            pLevelInfo->mana   = fields[3].GetUInt16();
1896            pLevelInfo->armor  = fields[9].GetUInt16();
1897
1898            for (int i = 0; i < MAX_STATS; i++)
1899            {
1900                pLevelInfo->stats[i] = fields[i+4].GetUInt16();
1901            }
1902
1903            bar.step();
1904            ++count;
1905        }
1906        while (result->NextRow());
1907
1908        delete result;
1909
1910        sLog.outString();
1911        sLog.outString( ">> Loaded %u level pet stats definitions", count );
1912    }
1913
1914    // Fill gaps and check integrity
1915    for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
1916    {
1917        PetLevelInfo* pInfo = itr->second;
1918
1919        // fatal error if no level 1 data
1920        if(!pInfo || pInfo[0].health == 0 )
1921        {
1922            sLog.outErrorDb("Creature %u does not have pet stats data for Level 1!",itr->first);
1923            exit(1);
1924        }
1925
1926        // fill level gaps
1927        for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
1928        {
1929            if(pInfo[level].health == 0)
1930            {
1931                sLog.outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.",itr->first,level+1, level);
1932                pInfo[level] = pInfo[level-1];
1933            }
1934        }
1935    }
1936}
1937
1938PetLevelInfo const* ObjectMgr::GetPetLevelInfo(uint32 creature_id, uint32 level) const
1939{
1940    if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
1941        level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
1942
1943    PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
1944    if(itr == petInfo.end())
1945        return NULL;
1946
1947    return &itr->second[level-1];                           // data for level 1 stored in [0] array element, ...
1948}
1949
1950void ObjectMgr::LoadPlayerInfo()
1951{
1952    // Load playercreate
1953    {
1954        //                                                0     1      2    3     4           5           6
1955        QueryResult *result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z FROM playercreateinfo");
1956
1957        uint32 count = 0;
1958
1959        if (!result)
1960        {
1961            barGoLink bar( 1 );
1962
1963            sLog.outString();
1964            sLog.outString( ">> Loaded %u player create definitions", count );
1965            sLog.outErrorDb( "Error loading `playercreateinfo` table or empty table.");
1966            exit(1);
1967        }
1968
1969        barGoLink bar( result->GetRowCount() );
1970
1971        do
1972        {
1973            Field* fields = result->Fetch();
1974
1975            uint32 current_race = fields[0].GetUInt32();
1976            uint32 current_class = fields[1].GetUInt32();
1977            uint32 mapId     = fields[2].GetUInt32();
1978            uint32 zoneId    = fields[3].GetUInt32();
1979            float  positionX = fields[4].GetFloat();
1980            float  positionY = fields[5].GetFloat();
1981            float  positionZ = fields[6].GetFloat();
1982
1983            if(current_race >= MAX_RACES)
1984            {
1985                sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
1986                continue;
1987            }
1988
1989            ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
1990            if(!rEntry)
1991            {
1992                sLog.outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.",current_race);
1993                continue;
1994            }
1995
1996            if(current_class >= MAX_CLASSES)
1997            {
1998                sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
1999                continue;
2000            }
2001
2002            if(!sChrClassesStore.LookupEntry(current_class))
2003            {
2004                sLog.outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.",current_class);
2005                continue;
2006            }
2007
2008            // accept DB data only for valid position (and non instanceable)
2009            if( !MapManager::IsValidMapCoord(mapId,positionX,positionY,positionZ) )
2010            {
2011                sLog.outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
2012                continue;
2013            }
2014
2015            if( sMapStore.LookupEntry(mapId)->Instanceable() )
2016            {
2017                sLog.outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.",current_class,current_race);
2018                continue;
2019            }
2020
2021            PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2022
2023            pInfo->mapId     = mapId;
2024            pInfo->zoneId    = zoneId;
2025            pInfo->positionX = positionX;
2026            pInfo->positionY = positionY;
2027            pInfo->positionZ = positionZ;
2028
2029            pInfo->displayId_m = rEntry->model_m;
2030            pInfo->displayId_f = rEntry->model_f;
2031
2032            bar.step();
2033            ++count;
2034        }
2035        while (result->NextRow());
2036
2037        delete result;
2038
2039        sLog.outString();
2040        sLog.outString( ">> Loaded %u player create definitions", count );
2041    }
2042
2043    // Load playercreate items
2044    {
2045        //                                                0     1      2       3
2046        QueryResult *result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");
2047
2048        uint32 count = 0;
2049
2050        if (!result)
2051        {
2052            barGoLink bar( 1 );
2053
2054            sLog.outString();
2055            sLog.outString( ">> Loaded %u player create items", count );
2056            sLog.outErrorDb( "Error loading `playercreateinfo_item` table or empty table.");
2057        }
2058        else
2059        {
2060            barGoLink bar( result->GetRowCount() );
2061
2062            do
2063            {
2064                Field* fields = result->Fetch();
2065
2066                uint32 current_race = fields[0].GetUInt32();
2067                if(current_race >= MAX_RACES)
2068                {
2069                    sLog.outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.",current_race);
2070                    continue;
2071                }
2072
2073                uint32 current_class = fields[1].GetUInt32();
2074                if(current_class >= MAX_CLASSES)
2075                {
2076                    sLog.outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.",current_class);
2077                    continue;
2078                }
2079
2080                PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2081
2082                uint32 item_id = fields[2].GetUInt32();
2083
2084                if(!GetItemPrototype(item_id))
2085                {
2086                    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);
2087                    continue;
2088                }
2089
2090                uint32 amount  = fields[3].GetUInt32();
2091
2092                if(!amount)
2093                {
2094                    sLog.outErrorDb("Item id %u (class %u race %u) have amount==0 in `playercreateinfo_item` table, ignoring.",item_id,current_race,current_class);
2095                    continue;
2096                }
2097
2098                pInfo->item.push_back(PlayerCreateInfoItem( item_id, amount));
2099
2100                bar.step();
2101                ++count;
2102            }
2103            while(result->NextRow());
2104
2105            delete result;
2106
2107            sLog.outString();
2108            sLog.outString( ">> Loaded %u player create items", count );
2109        }
2110    }
2111
2112    // Load playercreate spells
2113    {
2114
2115        QueryResult *result = NULL;
2116        if(sWorld.getConfig(CONFIG_START_ALL_SPELLS))
2117            result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom");
2118        else
2119            result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell");
2120
2121        uint32 count = 0;
2122
2123        if (!result)
2124        {
2125            barGoLink bar( 1 );
2126
2127            sLog.outString();
2128            sLog.outString( ">> Loaded %u player create spells", count );
2129            sLog.outErrorDb( "Error loading player starting spells or empty table.");
2130        }
2131        else
2132        {
2133            barGoLink bar( result->GetRowCount() );
2134
2135            do
2136            {
2137                Field* fields = result->Fetch();
2138
2139                uint32 current_race = fields[0].GetUInt32();
2140                if(current_race >= MAX_RACES)
2141                {
2142                    sLog.outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.",current_race);
2143                    continue;
2144                }
2145
2146                uint32 current_class = fields[1].GetUInt32();
2147                if(current_class >= MAX_CLASSES)
2148                {
2149                    sLog.outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.",current_class);
2150                    continue;
2151                }
2152
2153                PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2154                pInfo->spell.push_back(CreateSpellPair(fields[2].GetUInt16(), fields[3].GetUInt8()));
2155
2156                bar.step();
2157                ++count;
2158            }
2159            while( result->NextRow() );
2160
2161            delete result;
2162
2163            sLog.outString();
2164            sLog.outString( ">> Loaded %u player create spells", count );
2165        }
2166    }
2167
2168    // Load playercreate actions
2169    {
2170        //                                                0     1      2       3       4     5
2171        QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type, misc FROM playercreateinfo_action");
2172
2173        uint32 count = 0;
2174
2175        if (!result)
2176        {
2177            barGoLink bar( 1 );
2178
2179            sLog.outString();
2180            sLog.outString( ">> Loaded %u player create actions", count );
2181            sLog.outErrorDb( "Error loading `playercreateinfo_action` table or empty table.");
2182        }
2183        else
2184        {
2185            barGoLink bar( result->GetRowCount() );
2186
2187            do
2188            {
2189                Field* fields = result->Fetch();
2190
2191                uint32 current_race = fields[0].GetUInt32();
2192                if(current_race >= MAX_RACES)
2193                {
2194                    sLog.outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.",current_race);
2195                    continue;
2196                }
2197
2198                uint32 current_class = fields[1].GetUInt32();
2199                if(current_class >= MAX_CLASSES)
2200                {
2201                    sLog.outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.",current_class);
2202                    continue;
2203                }
2204
2205                PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2206                pInfo->action[0].push_back(fields[2].GetUInt16());
2207                pInfo->action[1].push_back(fields[3].GetUInt16());
2208                pInfo->action[2].push_back(fields[4].GetUInt16());
2209                pInfo->action[3].push_back(fields[5].GetUInt16());
2210
2211                bar.step();
2212                ++count;
2213            }
2214            while( result->NextRow() );
2215
2216            delete result;
2217
2218            sLog.outString();
2219            sLog.outString( ">> Loaded %u player create actions", count );
2220        }
2221    }
2222
2223    // Loading levels data (class only dependent)
2224    {
2225        //                                                 0      1      2       3
2226        QueryResult *result  = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
2227
2228        uint32 count = 0;
2229
2230        if (!result)
2231        {
2232            barGoLink bar( 1 );
2233
2234            sLog.outString();
2235            sLog.outString( ">> Loaded %u level health/mana definitions", count );
2236            sLog.outErrorDb( "Error loading `player_classlevelstats` table or empty table.");
2237            exit(1);
2238        }
2239
2240        barGoLink bar( result->GetRowCount() );
2241
2242        do
2243        {
2244            Field* fields = result->Fetch();
2245
2246            uint32 current_class = fields[0].GetUInt32();
2247            if(current_class >= MAX_CLASSES)
2248            {
2249                sLog.outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.",current_class);
2250                continue;
2251            }
2252
2253            uint32 current_level = fields[1].GetUInt32();
2254            if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2255            {
2256                if(current_level > 255)                     // hardcoded level maximum
2257                    sLog.outErrorDb("Wrong (> 255) level %u in `player_classlevelstats` table, ignoring.",current_level);
2258                else
2259                    sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `player_classlevelstats` table, ignoring.",current_level);
2260                continue;
2261            }
2262
2263            PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];
2264
2265            if(!pClassInfo->levelInfo)
2266                pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2267
2268            PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level-1];
2269
2270            pClassLevelInfo->basehealth = fields[2].GetUInt16();
2271            pClassLevelInfo->basemana   = fields[3].GetUInt16();
2272
2273            bar.step();
2274            ++count;
2275        }
2276        while (result->NextRow());
2277
2278        delete result;
2279
2280        sLog.outString();
2281        sLog.outString( ">> Loaded %u level health/mana definitions", count );
2282    }
2283
2284    // Fill gaps and check integrity
2285    for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2286    {
2287        // skip non existed classes
2288        if(!sChrClassesStore.LookupEntry(class_))
2289            continue;
2290
2291        PlayerClassInfo* pClassInfo = &playerClassInfo[class_];
2292
2293        // fatal error if no level 1 data
2294        if(!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0 )
2295        {
2296            sLog.outErrorDb("Class %i Level 1 does not have health/mana data!",class_);
2297            exit(1);
2298        }
2299
2300        // fill level gaps
2301        for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2302        {
2303            if(pClassInfo->levelInfo[level].basehealth == 0)
2304            {
2305                sLog.outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.",class_,level+1, level);
2306                pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level-1];
2307            }
2308        }
2309    }
2310
2311    // Loading levels data (class/race dependent)
2312    {
2313        //                                                 0     1      2      3    4    5    6    7
2314        QueryResult *result  = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");
2315
2316        uint32 count = 0;
2317
2318        if (!result)
2319        {
2320            barGoLink bar( 1 );
2321
2322            sLog.outString();
2323            sLog.outString( ">> Loaded %u level stats definitions", count );
2324            sLog.outErrorDb( "Error loading `player_levelstats` table or empty table.");
2325            exit(1);
2326        }
2327
2328        barGoLink bar( result->GetRowCount() );
2329
2330        do
2331        {
2332            Field* fields = result->Fetch();
2333
2334            uint32 current_race = fields[0].GetUInt32();
2335            if(current_race >= MAX_RACES)
2336            {
2337                sLog.outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.",current_race);
2338                continue;
2339            }
2340
2341            uint32 current_class = fields[1].GetUInt32();
2342            if(current_class >= MAX_CLASSES)
2343            {
2344                sLog.outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.",current_class);
2345                continue;
2346            }
2347
2348            uint32 current_level = fields[2].GetUInt32();
2349            if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2350            {
2351                if(current_level > 255)                     // hardcoded level maximum
2352                    sLog.outErrorDb("Wrong (> 255) level %u in `player_levelstats` table, ignoring.",current_level);
2353                else
2354                    sLog.outDetail("Unused (> MaxPlayerLevel in Trinityd.conf) level %u in `player_levelstats` table, ignoring.",current_level);
2355                continue;
2356            }
2357
2358            PlayerInfo* pInfo = &playerInfo[current_race][current_class];
2359
2360            if(!pInfo->levelInfo)
2361                pInfo->levelInfo = new PlayerLevelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)];
2362
2363            PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level-1];
2364
2365            for (int i = 0; i < MAX_STATS; i++)
2366            {
2367                pLevelInfo->stats[i] = fields[i+3].GetUInt8();
2368            }
2369
2370            bar.step();
2371            ++count;
2372        }
2373        while (result->NextRow());
2374
2375        delete result;
2376
2377        sLog.outString();
2378        sLog.outString( ">> Loaded %u level stats definitions", count );
2379    }
2380
2381    // Fill gaps and check integrity
2382    for (int race = 0; race < MAX_RACES; ++race)
2383    {
2384        // skip non existed races
2385        if(!sChrRacesStore.LookupEntry(race))
2386            continue;
2387
2388        for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
2389        {
2390            // skip non existed classes
2391            if(!sChrClassesStore.LookupEntry(class_))
2392                continue;
2393
2394            PlayerInfo* pInfo = &playerInfo[race][class_];
2395
2396            // skip non loaded combinations
2397            if(!pInfo->displayId_m || !pInfo->displayId_f)
2398                continue;
2399
2400            // skip expansion races if not playing with expansion
2401            if (sWorld.getConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
2402                continue;
2403               
2404            // skip expansion classes if not playing with expansion
2405            if (sWorld.getConfig(CONFIG_EXPANSION) < 2 && class_ == CLASS_DEATH_KNIGHT)
2406                continue;
2407
2408            // fatal error if no level 1 data
2409            if(!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0 )
2410            {
2411                sLog.outErrorDb("Race %i Class %i Level 1 does not have stats data!",race,class_);
2412                exit(1);
2413            }
2414
2415            // fill level gaps
2416            for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
2417            {
2418                if(pInfo->levelInfo[level].stats[0] == 0)
2419                {
2420                    sLog.outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.",race,class_,level+1, level);
2421                    pInfo->levelInfo[level] = pInfo->levelInfo[level-1];
2422                }
2423            }
2424        }
2425    }
2426}
2427
2428void ObjectMgr::GetPlayerClassLevelInfo(uint32 class_, uint32 level, PlayerClassLevelInfo* info) const
2429{
2430    if(level < 1 || class_ >= MAX_CLASSES)
2431        return;
2432
2433    PlayerClassInfo const* pInfo = &playerClassInfo[class_];
2434
2435    if(level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2436        level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL);
2437
2438    *info = pInfo->levelInfo[level-1];
2439}
2440
2441void ObjectMgr::GetPlayerLevelInfo(uint32 race, uint32 class_, uint32 level, PlayerLevelInfo* info) const
2442{
2443    if(level < 1 || race   >= MAX_RACES || class_ >= MAX_CLASSES)
2444        return;
2445
2446    PlayerInfo const* pInfo = &playerInfo[race][class_];
2447    if(pInfo->displayId_m==0 || pInfo->displayId_f==0)
2448        return;
2449
2450    if(level <= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
2451        *info = pInfo->levelInfo[level-1];
2452    else
2453        BuildPlayerLevelInfo(race,class_,level,info);
2454}
2455
2456void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
2457{
2458    // base data (last known level)
2459    *info = playerInfo[race][_class].levelInfo[sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1];
2460
2461    for(int lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
2462    {
2463        switch(_class)
2464        {
2465            case CLASS_WARRIOR:
2466                info->stats[STAT_STRENGTH]  += (lvl > 23 ? 2: (lvl > 1  ? 1: 0));
2467                info->stats[STAT_STAMINA]   += (lvl > 23 ? 2: (lvl > 1  ? 1: 0));
2468                info->stats[STAT_AGILITY]   += (lvl > 36 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
2469                info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl%2) ? 1: 0);
2470                info->stats[STAT_SPIRIT]    += (lvl > 9 && !(lvl%2) ? 1: 0);
2471                break;
2472            case CLASS_PALADIN:
2473                info->stats[STAT_STRENGTH]  += (lvl > 3  ? 1: 0);
2474                info->stats[STAT_STAMINA]   += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
2475                info->stats[STAT_AGILITY]   += (lvl > 38 ? 1: (lvl > 7 && !(lvl%2) ? 1: 0));
2476                info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl%2) ? 1: 0);
2477                info->stats[STAT_SPIRIT]    += (lvl > 7 ? 1: 0);
2478                break;
2479            case CLASS_HUNTER:
2480                info->stats[STAT_STRENGTH]  += (lvl > 4  ? 1: 0);
2481                info->stats[STAT_STAMINA]   += (lvl > 4  ? 1: 0);
2482                info->stats[STAT_AGILITY]   += (lvl > 33 ? 2: (lvl > 1 ? 1: 0));
2483                info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl%2) ? 1: 0);
2484                info->stats[STAT_SPIRIT]    += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
2485                break;
2486            case CLASS_ROGUE:
2487                info->stats[STAT_STRENGTH]  += (lvl > 5  ? 1: 0);
2488                info->stats[STAT_STAMINA]   += (lvl > 4  ? 1: 0);
2489                info->stats[STAT_AGILITY]   += (lvl > 16 ? 2: (lvl > 1 ? 1: 0));
2490                info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl%2) ? 1: 0);
2491                info->stats[STAT_SPIRIT]    += (lvl > 38 ? 1: (lvl > 9 && !(lvl%2) ? 1: 0));
2492                break;
2493            case CLASS_PRIEST:
2494                info->stats[STAT_STRENGTH]  += (lvl > 9 && !(lvl%2) ? 1: 0);
2495                info->stats[STAT_STAMINA]   += (lvl > 5  ? 1: 0);
2496                info->stats[STAT_AGILITY]   += (lvl > 38 ? 1: (lvl > 8 && (lvl%2) ? 1: 0));
2497                info->stats[STAT_INTELLECT] += (lvl > 22 ? 2: (lvl > 1 ? 1: 0));
2498                info->stats[STAT_SPIRIT]    += (lvl > 3  ? 1: 0);
2499                break;
2500            case CLASS_SHAMAN:
2501                info->stats[STAT_STRENGTH]  += (lvl > 34 ? 1: (lvl > 6 && (lvl%2) ? 1: 0));
2502                info->stats[STAT_STAMINA]   += (lvl > 4 ? 1: 0);
2503                info->stats[STAT_AGILITY]   += (lvl > 7 && !(lvl%2) ? 1: 0);
2504                info->stats[STAT_INTELLECT] += (lvl > 5 ? 1: 0);
2505                info->stats[STAT_SPIRIT]    += (lvl > 4 ? 1: 0);
2506                break;
2507            case CLASS_MAGE:
2508                info->stats[STAT_STRENGTH]  += (lvl > 9 && !(lvl%2) ? 1: 0);
2509                info->stats[STAT_STAMINA]   += (lvl > 5  ? 1: 0);
2510                info->stats[STAT_AGILITY]   += (lvl > 9 && !(lvl%2) ? 1: 0);
2511                info->stats[STAT_INTELLECT] += (lvl > 24 ? 2: (lvl > 1 ? 1: 0));
2512                info->stats[STAT_SPIRIT]    += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
2513                break;
2514            case CLASS_WARLOCK:
2515                info->stats[STAT_STRENGTH]  += (lvl > 9 && !(lvl%2) ? 1: 0);
2516                info->stats[STAT_STAMINA]   += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
2517                info->stats[STAT_AGILITY]   += (lvl > 9 && !(lvl%2) ? 1: 0);
2518                info->stats[STAT_INTELLECT] += (lvl > 33 ? 2: (lvl > 2 ? 1: 0));
2519                info->stats[STAT_SPIRIT]    += (lvl > 38 ? 2: (lvl > 3 ? 1: 0));
2520                break;
2521            case CLASS_DRUID:
2522                info->stats[STAT_STRENGTH]  += (lvl > 38 ? 2: (lvl > 6 && (lvl%2) ? 1: 0));
2523                info->stats[STAT_STAMINA]   += (lvl > 32 ? 2: (lvl > 4 ? 1: 0));
2524                info->stats[STAT_AGILITY]   += (lvl > 38 ? 2: (lvl > 8 && (lvl%2) ? 1: 0));
2525                info->stats[STAT_INTELLECT] += (lvl > 38 ? 3: (lvl > 4 ? 1: 0));
2526                info->stats[STAT_SPIRIT]    += (lvl > 38 ? 3: (lvl > 5 ? 1: 0));
2527        }
2528    }
2529}
2530
2531void ObjectMgr::LoadGuilds()
2532{
2533    Guild *newguild;
2534    uint32 count = 0;
2535
2536    QueryResult *result = CharacterDatabase.Query( "SELECT guildid FROM guild" );
2537
2538    if( !result )
2539    {
2540
2541        barGoLink bar( 1 );
2542
2543        bar.step();
2544
2545        sLog.outString();
2546        sLog.outString( ">> Loaded %u guild definitions", count );
2547        return;
2548    }
2549
2550    barGoLink bar( result->GetRowCount() );
2551
2552    do
2553    {
2554        Field *fields = result->Fetch();
2555
2556        bar.step();
2557        ++count;
2558
2559        newguild = new Guild;
2560        if(!newguild->LoadGuildFromDB(fields[0].GetUInt32()))
2561        {
2562            newguild->Disband();
2563            delete newguild;
2564            continue;
2565        }
2566        AddGuild(newguild);
2567
2568    }while( result->NextRow() );
2569
2570    delete result;
2571
2572    sLog.outString();
2573    sLog.outString( ">> Loaded %u guild definitions", count );
2574}
2575
2576void ObjectMgr::LoadArenaTeams()
2577{
2578    uint32 count = 0;
2579
2580    QueryResult *result = CharacterDatabase.Query( "SELECT arenateamid FROM arena_team" );
2581
2582    if( !result )
2583    {
2584
2585        barGoLink bar( 1 );
2586
2587        bar.step();
2588
2589        sLog.outString();
2590        sLog.outString( ">> Loaded %u arenateam definitions", count );
2591        return;
2592    }
2593
2594    barGoLink bar( result->GetRowCount() );
2595
2596    do
2597    {
2598        Field *fields = result->Fetch();
2599
2600        bar.step();
2601        ++count;
2602
2603        ArenaTeam *newarenateam = new ArenaTeam;
2604        if(!newarenateam->LoadArenaTeamFromDB(fields[0].GetUInt32()))
2605        {
2606            delete newarenateam;
2607            continue;
2608        }
2609        AddArenaTeam(newarenateam);
2610    }while( result->NextRow() );
2611
2612    delete result;
2613
2614    sLog.outString();
2615    sLog.outString( ">> Loaded %u arenateam definitions", count );
2616}
2617
2618void ObjectMgr::LoadGroups()
2619{
2620    // -- loading groups --
2621    Group *group = NULL;
2622    uint64 leaderGuid = 0;
2623    uint32 count = 0;
2624    //                                                     0         1              2           3           4              5      6      7      8      9      10     11     12     13      14          15
2625    QueryResult *result = CharacterDatabase.PQuery("SELECT mainTank, mainAssistant, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, isRaid, difficulty, leaderGuid FROM groups");
2626
2627    if( !result )
2628    {
2629        barGoLink bar( 1 );
2630
2631        bar.step();
2632
2633        sLog.outString();
2634        sLog.outString( ">> Loaded %u group definitions", count );
2635        return;
2636    }
2637
2638    barGoLink bar( result->GetRowCount() );
2639
2640    do
2641    {
2642        bar.step();
2643        Field *fields = result->Fetch();
2644        ++count;
2645        leaderGuid = MAKE_NEW_GUID(fields[15].GetUInt32(),0,HIGHGUID_PLAYER);
2646
2647        group = new Group;
2648        if(!group->LoadGroupFromDB(leaderGuid, result, false))
2649        {
2650            group->Disband();
2651            delete group;
2652            continue;
2653        }
2654        AddGroup(group);
2655    }while( result->NextRow() );
2656
2657    delete result;
2658
2659    sLog.outString();
2660    sLog.outString( ">> Loaded %u group definitions", count );
2661
2662    // -- loading members --
2663    count = 0;
2664    group = NULL;
2665    leaderGuid = 0;
2666    //                                        0           1          2         3
2667    result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup, leaderGuid FROM group_member ORDER BY leaderGuid");
2668    if(!result)
2669    {
2670        barGoLink bar( 1 );
2671        bar.step();
2672    }
2673    else
2674    {
2675        barGoLink bar( result->GetRowCount() );
2676        do
2677        {
2678            bar.step();
2679            Field *fields = result->Fetch();
2680            count++;
2681            leaderGuid = MAKE_NEW_GUID(fields[3].GetUInt32(), 0, HIGHGUID_PLAYER);
2682            if(!group || group->GetLeaderGUID() != leaderGuid)
2683            {
2684                group = GetGroupByLeader(leaderGuid);
2685                if(!group)
2686                {
2687                    sLog.outErrorDb("Incorrect entry in group_member table : no group with leader %d for member %d!", fields[3].GetUInt32(), fields[0].GetUInt32());
2688                    CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
2689                    continue;
2690                }
2691            }
2692
2693            if(!group->LoadMemberFromDB(fields[0].GetUInt32(), fields[2].GetUInt8(), fields[1].GetBool()))
2694            {
2695                sLog.outErrorDb("Incorrect entry in group_member table : member %d cannot be added to player %d's group!", fields[0].GetUInt32(), fields[3].GetUInt32());
2696                CharacterDatabase.PExecute("DELETE FROM group_member WHERE memberGuid = '%d'", fields[0].GetUInt32());
2697            }
2698        }while( result->NextRow() );
2699        delete result;
2700    }
2701
2702    // clean groups
2703    // TODO: maybe delete from the DB before loading in this case
2704    for(GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end();)
2705    {
2706        if((*itr)->GetMembersCount() < 2)
2707        {
2708            (*itr)->Disband();
2709            delete *itr;
2710            mGroupSet.erase(itr++);
2711        }
2712        else
2713            ++itr;
2714    }
2715
2716    // -- loading instances --
2717    count = 0;
2718    group = NULL;
2719    leaderGuid = 0;
2720    result = CharacterDatabase.PQuery(
2721        //      0           1    2         3          4           5
2722        "SELECT leaderGuid, map, instance, permanent, difficulty, resettime, "
2723        // 6
2724        "(SELECT COUNT(*) FROM character_instance WHERE guid = leaderGuid AND instance = group_instance.instance AND permanent = 1 LIMIT 1) "
2725        "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY leaderGuid"
2726    );
2727
2728    if(!result)
2729    {
2730        barGoLink bar( 1 );
2731        bar.step();
2732    }
2733    else
2734    {
2735        barGoLink bar( result->GetRowCount() );
2736        do
2737        {
2738            bar.step();
2739            Field *fields = result->Fetch();
2740            count++;
2741            leaderGuid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
2742            if(!group || group->GetLeaderGUID() != leaderGuid)
2743            {
2744                group = GetGroupByLeader(leaderGuid);
2745                if(!group)
2746                {
2747                    sLog.outErrorDb("Incorrect entry in group_instance table : no group with leader %d", fields[0].GetUInt32());
2748                    continue;
2749                }
2750            }
2751
2752            InstanceSave *save = sInstanceSaveManager.AddInstanceSave(fields[1].GetUInt32(), fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
2753            group->BindToInstance(save, fields[3].GetBool(), true);
2754        }while( result->NextRow() );
2755        delete result;
2756    }
2757
2758    sLog.outString();
2759    sLog.outString( ">> Loaded %u group-instance binds total", count );
2760
2761    sLog.outString();
2762    sLog.outString( ">> Loaded %u group members total", count );
2763}
2764
2765void ObjectMgr::LoadQuests()
2766{
2767    // For reload case
2768    for(QuestMap::const_iterator itr=mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
2769        delete itr->second;
2770    mQuestTemplates.clear();
2771
2772    mExclusiveQuestGroups.clear();
2773
2774    //                                                0      1       2           3             4         5           6     7              8
2775    QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
2776    //   9                    10                 11                     12                   13                     14                   15                16
2777        "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
2778    //   17          18            19           20           21           22              23                24         25            26
2779        "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
2780    //   27     28       29          30               31                32       33              34              35              36
2781        "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
2782    //   37          38          39          40          41             42             43             44
2783        "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
2784    //   45            46            47            48            49               50               51               52               53             54             54             55
2785        "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4,"
2786    //   57                  58                  59                  60                  61                     62                     63                     64
2787        "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
2788    //   65             66             67             68
2789        "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
2790    //   69                70                71                72                73                74
2791        "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
2792    //   75                   76                   77                   78                   79                   80
2793        "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
2794    //   81          82          83          84          85             86             87             88
2795        "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
2796    //   89              90              91              92              93              94            95            96            97            98
2797        "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
2798    //   99             100               101       102           103                104               105         106     107     108
2799        "RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
2800    //   109            110            111            112           113              114            115                116                117                118
2801        "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
2802    //   119          120
2803        "StartScript, CompleteScript"
2804        " FROM quest_template");
2805    if(result == NULL)
2806    {
2807        barGoLink bar( 1 );
2808        bar.step();
2809
2810        sLog.outString();
2811        sLog.outString( ">> Loaded 0 quests definitions" );
2812        sLog.outErrorDb("`quest_template` table is empty!");
2813        return;
2814    }
2815
2816    // create multimap previous quest for each existed quest
2817    // some quests can have many previous maps set by NextQuestId in previous quest
2818    // for example set of race quests can lead to single not race specific quest
2819    barGoLink bar( result->GetRowCount() );
2820    do
2821    {
2822        bar.step();
2823        Field *fields = result->Fetch();
2824
2825        Quest * newQuest = new Quest(fields);
2826        mQuestTemplates[newQuest->GetQuestId()] = newQuest;
2827    } while( result->NextRow() );
2828
2829    delete result;
2830
2831    // Post processing
2832    for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); iter++)
2833    {
2834        Quest * qinfo = iter->second;
2835
2836        // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
2837
2838        if( qinfo->GetQuestMethod() >= 3 )
2839        {
2840            sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
2841        }
2842
2843        if (qinfo->QuestFlags & ~QUEST_TRINITY_FLAGS_DB_ALLOWED)
2844        {
2845            sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
2846                qinfo->GetQuestId(),qinfo->QuestFlags,QUEST_TRINITY_FLAGS_DB_ALLOWED >> 16);
2847            qinfo->QuestFlags &= QUEST_TRINITY_FLAGS_DB_ALLOWED;
2848        }
2849
2850        if(qinfo->QuestFlags & QUEST_FLAGS_DAILY)
2851        {
2852            if(!(qinfo->QuestFlags & QUEST_TRINITY_FLAGS_REPEATABLE))
2853            {
2854                sLog.outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.",qinfo->GetQuestId());
2855                qinfo->QuestFlags |= QUEST_TRINITY_FLAGS_REPEATABLE;
2856            }
2857        }
2858
2859        if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED)
2860        {
2861            // at auto-reward can be rewarded only RewChoiceItemId[0]
2862            for(int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j )
2863            {
2864                if(uint32 id = qinfo->RewChoiceItemId[j])
2865                {
2866                    sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.",
2867                        qinfo->GetQuestId(),j+1,id,j+1);
2868                    // no changes, quest ignore this data
2869                }
2870            }
2871        }
2872
2873        // client quest log visual (area case)
2874        if( qinfo->ZoneOrSort > 0 )
2875        {
2876            if(!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
2877            {
2878                sLog.outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.",
2879                    qinfo->GetQuestId(),qinfo->ZoneOrSort);
2880                // no changes, quest not dependent from this value but can have problems at client
2881            }
2882        }
2883        // client quest log visual (sort case)
2884        if( qinfo->ZoneOrSort < 0 )
2885        {
2886            QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
2887            if( !qSort )
2888            {
2889                sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.",
2890                    qinfo->GetQuestId(),qinfo->ZoneOrSort);
2891                // 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)
2892            }
2893            //check SkillOrClass value (class case).
2894            if( ClassByQuestSort(-int32(qinfo->ZoneOrSort)) )
2895            {
2896                // SkillOrClass should not have class case when class case already set in ZoneOrSort.
2897                if(qinfo->SkillOrClass < 0)
2898                {
2899                    sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (class sort case) and `SkillOrClass` = %i (class case), redundant.",
2900                        qinfo->GetQuestId(),qinfo->ZoneOrSort,qinfo->SkillOrClass);
2901                }
2902            }
2903            //check for proper SkillOrClass value (skill case)
2904            if(int32 skill_id =  SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
2905            {
2906                // skill is positive value in SkillOrClass
2907                if(qinfo->SkillOrClass != skill_id )
2908                {
2909                    sLog.outErrorDb("Quest %u has `ZoneOrSort` = %i (skill sort case) but `SkillOrClass` does not have a corresponding value (%i).",
2910                        qinfo->GetQuestId(),qinfo->ZoneOrSort,skill_id);
2911                    //override, and force proper value here?
2912                }
2913            }
2914        }
2915
2916        // SkillOrClass (class case)
2917        if( qinfo->SkillOrClass < 0 )
2918        {
2919            if( !sChrClassesStore.LookupEntry(-int32(qinfo->SkillOrClass)) )
2920            {
2921                sLog.outErrorDb("Quest %u has `SkillOrClass` = %i (class case) but class (%i) does not exist",
2922                    qinfo->GetQuestId(),qinfo->SkillOrClass,-qinfo->SkillOrClass);
2923            }
2924        }
2925        // SkillOrClass (skill case)
2926        if( qinfo->SkillOrClass > 0 )
2927        {
2928            if( !sSkillLineStore.LookupEntry(qinfo->SkillOrClass) )
2929            {
2930                sLog.outErrorDb("Quest %u has `SkillOrClass` = %u (skill case) but skill (%i) does not exist",
2931                    qinfo->GetQuestId(),qinfo->SkillOrClass,qinfo->SkillOrClass);
2932            }
2933        }
2934
2935        if( qinfo->RequiredSkillValue )
2936        {
2937            if( qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue() )
2938            {
2939                sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.",
2940                    qinfo->GetQuestId(),qinfo->RequiredSkillValue,sWorld.GetConfigMaxSkillValue());
2941                // no changes, quest can't be done for this requirement
2942            }
2943
2944            if( qinfo->SkillOrClass <= 0 )
2945            {
2946                sLog.outErrorDb("Quest %u has `RequiredSkillValue` = %u but `SkillOrClass` = %i (class case), value ignored.",
2947                    qinfo->GetQuestId(),qinfo->RequiredSkillValue,qinfo->SkillOrClass);
2948                // no changes, quest can't be done for this requirement (fail at wrong skill id)
2949            }
2950        }
2951        // else Skill quests can have 0 skill level, this is ok
2952
2953        if(qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
2954        {
2955            sLog.outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.",
2956                qinfo->GetQuestId(),qinfo->RepObjectiveFaction,qinfo->RepObjectiveFaction);
2957            // no changes, quest can't be done for this requirement
2958        }
2959
2960        if(qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
2961        {
2962            sLog.outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.",
2963                qinfo->GetQuestId(),qinfo->RequiredMinRepFaction,qinfo->RequiredMinRepFaction);
2964            // no changes, quest can't be done for this requirement
2965        }
2966
2967        if(qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
2968        {
2969            sLog.outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.",
2970                qinfo->GetQuestId(),qinfo->RequiredMaxRepFaction,qinfo->RequiredMaxRepFaction);
2971            // no changes, quest can't be done for this requirement
2972        }
2973
2974        if(qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > Player::Reputation_Cap)
2975        {
2976            sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
2977                qinfo->GetQuestId(),qinfo->RequiredMinRepValue,Player::Reputation_Cap);
2978            // no changes, quest can't be done for this requirement
2979        }
2980
2981        if(qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
2982        {
2983            sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.",
2984                qinfo->GetQuestId(),qinfo->RequiredMaxRepValue,qinfo->RequiredMinRepValue);
2985            // no changes, quest can't be done for this requirement
2986        }
2987
2988        if(!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0 )
2989        {
2990            sLog.outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect",
2991                qinfo->GetQuestId(),qinfo->RepObjectiveValue);
2992            // warning
2993        }
2994
2995        if(!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0 )
2996        {
2997            sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect",
2998                qinfo->GetQuestId(),qinfo->RequiredMinRepValue);
2999            // warning
3000        }
3001
3002        if(!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0 )
3003        {
3004            sLog.outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect",
3005                qinfo->GetQuestId(),qinfo->RequiredMaxRepValue);
3006            // warning
3007        }
3008
3009        if(qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
3010        {
3011            sLog.outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.",
3012                qinfo->GetQuestId(),qinfo->GetCharTitleId(),qinfo->GetCharTitleId());
3013            qinfo->CharTitleId = 0;
3014            // quest can't reward this title
3015        }
3016
3017        if(qinfo->SrcItemId)
3018        {
3019            if(!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
3020            {
3021                sLog.outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.",
3022                    qinfo->GetQuestId(),qinfo->SrcItemId,qinfo->SrcItemId);
3023                qinfo->SrcItemId = 0;                       // quest can't be done for this requirement
3024            }
3025            else if(qinfo->SrcItemCount==0)
3026            {
3027                sLog.outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.",
3028                    qinfo->GetQuestId(),qinfo->SrcItemId);
3029                qinfo->SrcItemCount = 1;                    // update to 1 for allow quest work for backward comptibility with DB
3030            }
3031        }
3032        else if(qinfo->SrcItemCount>0)
3033        {
3034            sLog.outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.",
3035                qinfo->GetQuestId(),qinfo->SrcItemCount);
3036            qinfo->SrcItemCount=0;                          // no quest work changes in fact
3037        }
3038
3039        if(qinfo->SrcSpell)
3040        {
3041            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
3042            if(!spellInfo)
3043            {
3044                sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.",
3045                    qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
3046                qinfo->SrcSpell = 0;                        // quest can't be done for this requirement
3047            }
3048            else if(!SpellMgr::IsSpellValid(spellInfo))
3049            {
3050                sLog.outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.",
3051                    qinfo->GetQuestId(),qinfo->SrcSpell,qinfo->SrcSpell);
3052                qinfo->SrcSpell = 0;                        // quest can't be done for this requirement
3053            }
3054        }
3055
3056        for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
3057        {
3058            uint32 id = qinfo->ReqItemId[j];
3059            if(id)
3060            {
3061                if(qinfo->ReqItemCount[j]==0)
3062                {
3063                    sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.",
3064                        qinfo->GetQuestId(),j+1,id,j+1);
3065                    // no changes, quest can't be done for this requirement
3066                }
3067
3068                qinfo->SetFlag(QUEST_TRINITY_FLAGS_DELIVER);
3069
3070                if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3071                {
3072                    sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
3073                        qinfo->GetQuestId(),j+1,id,id);
3074                    qinfo->ReqItemCount[j] = 0;             // prevent incorrect work of quest
3075                }
3076            }
3077            else if(qinfo->ReqItemCount[j]>0)
3078            {
3079                sLog.outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.",
3080                    qinfo->GetQuestId(),j+1,j+1,qinfo->ReqItemCount[j]);
3081                qinfo->ReqItemCount[j] = 0;                 // prevent incorrect work of quest
3082            }
3083        }
3084
3085        for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
3086        {
3087            uint32 id = qinfo->ReqSourceId[j];
3088            if(id)
3089            {
3090                if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3091                {
3092                    sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.",
3093                        qinfo->GetQuestId(),j+1,id,id);
3094                    // no changes, quest can't be done for this requirement
3095                }
3096
3097                if(!qinfo->ReqSourceCount[j])
3098                {
3099                    sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.",
3100                        qinfo->GetQuestId(),j+1,id,j+1);
3101                    qinfo->ReqSourceId[j] = 0;              // prevent incorrect work of quest
3102                }
3103
3104                if(!qinfo->ReqSourceRef[j])
3105                {
3106                    sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.",
3107                        qinfo->GetQuestId(),j+1,id,j+1);
3108                    qinfo->ReqSourceId[j] = 0;              // prevent incorrect work of quest
3109                }
3110            }
3111            else
3112            {
3113                if(qinfo->ReqSourceCount[j]>0)
3114                {
3115                    sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.",
3116                        qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]);
3117                    // no changes, quest ignore this data
3118                }
3119
3120                if(qinfo->ReqSourceRef[j]>0)
3121                {
3122                    sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.",
3123                        qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]);
3124                    // no changes, quest ignore this data
3125                }
3126            }
3127        }
3128
3129        for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
3130        {
3131            uint32 ref = qinfo->ReqSourceRef[j];
3132            if(ref)
3133            {
3134                if(ref > QUEST_OBJECTIVES_COUNT)
3135                {
3136                    sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.",
3137                        qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT);
3138                    // no changes, quest can't be done for this requirement
3139                }
3140                else
3141                if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1])
3142                {
3143                    sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.",
3144                        qinfo->GetQuestId(),j+1,ref,ref,ref);
3145                    // no changes, quest can't be done for this requirement
3146                }
3147                else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1])
3148                {
3149                    sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.",
3150                        qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]);
3151                    // no changes, quest can't be done for this requirement
3152                    qinfo->ReqSourceId[j] = 0;              // prevent incorrect work of quest
3153                }
3154            }
3155        }
3156
3157        for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
3158        {
3159            uint32 id = qinfo->ReqSpell[j];
3160            if(id)
3161            {
3162                SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
3163                if(!spellInfo)
3164                {
3165                    sLog.outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.",
3166                        qinfo->GetQuestId(),j+1,id,id);
3167                    // no changes, quest can't be done for this requirement
3168                }
3169
3170                if(!qinfo->ReqCreatureOrGOId[j])
3171                {
3172                    bool found = false;
3173                    for(int k = 0; k < 3; ++k)
3174                    {
3175                        if( spellInfo->Effect[k]==SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k])==qinfo->QuestId ||
3176                            spellInfo->Effect[k]==SPELL_EFFECT_SEND_EVENT)
3177                        {
3178                            found = true;
3179                            break;
3180                        }
3181                    }
3182
3183                    if(found)
3184                    {
3185                        if(!qinfo->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
3186                        {
3187                            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_TRINITY_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);
3188
3189                            // this will prevent quest completing without objective
3190                            const_cast<Quest*>(qinfo)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
3191                        }
3192                    }
3193                    else
3194                    {
3195                        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.",
3196                            qinfo->GetQuestId(),j+1,id,j+1,id);
3197                        // no changes, quest can't be done for this requirement
3198                    }
3199                }
3200            }
3201        }
3202
3203        for(int j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
3204        {
3205            int32 id = qinfo->ReqCreatureOrGOId[j];
3206            if(id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
3207            {
3208                sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.",
3209                    qinfo->GetQuestId(),j+1,id,uint32(-id));
3210                qinfo->ReqCreatureOrGOId[j] = 0;            // quest can't be done for this requirement
3211            }
3212
3213            if(id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
3214            {
3215                sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
3216                    qinfo->GetQuestId(),j+1,id,uint32(id));
3217                qinfo->ReqCreatureOrGOId[j] = 0;            // quest can't be done for this requirement
3218            }
3219
3220            if(id)
3221            {
3222                // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
3223
3224                qinfo->SetFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO);
3225
3226                if(!qinfo->ReqCreatureOrGOCount[j])
3227                {
3228                    sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
3229                        qinfo->GetQuestId(),j+1,id,j+1);
3230                    // no changes, quest can be incorrectly done, but we already report this
3231                }
3232            }
3233            else if(qinfo->ReqCreatureOrGOCount[j]>0)
3234            {
3235                sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.",
3236                    qinfo->GetQuestId(),j+1,j+1,qinfo->ReqCreatureOrGOCount[j]);
3237                // no changes, quest ignore this data
3238            }
3239        }
3240
3241        for(int j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j )
3242        {
3243            uint32 id = qinfo->RewChoiceItemId[j];
3244            if(id)
3245            {
3246                if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3247                {
3248                    sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
3249                        qinfo->GetQuestId(),j+1,id,id);
3250                    qinfo->RewChoiceItemId[j] = 0;          // no changes, quest will not reward this
3251                }
3252
3253                if(!qinfo->RewChoiceItemCount[j])
3254                {
3255                    sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.",
3256                        qinfo->GetQuestId(),j+1,id,j+1);
3257                    // no changes, quest can't be done
3258                }
3259            }
3260            else if(qinfo->RewChoiceItemCount[j]>0)
3261            {
3262                sLog.outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.",
3263                    qinfo->GetQuestId(),j+1,j+1,qinfo->RewChoiceItemCount[j]);
3264                // no changes, quest ignore this data
3265            }
3266        }
3267
3268        for(int j = 0; j < QUEST_REWARDS_COUNT; ++j )
3269        {
3270            uint32 id = qinfo->RewItemId[j];
3271            if(id)
3272            {
3273                if(!sItemStorage.LookupEntry<ItemPrototype>(id))
3274                {
3275                    sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.",
3276                        qinfo->GetQuestId(),j+1,id,id);
3277                    qinfo->RewItemId[j] = 0;                // no changes, quest will not reward this item
3278                }
3279
3280                if(!qinfo->RewItemCount[j])
3281                {
3282                    sLog.outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.",
3283                        qinfo->GetQuestId(),j+1,id,j+1);
3284                    // no changes
3285                }
3286            }
3287            else if(qinfo->RewItemCount[j]>0)
3288            {
3289                sLog.outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.",
3290                    qinfo->GetQuestId(),j+1,j+1,qinfo->RewItemCount[j]);
3291                // no changes, quest ignore this data
3292            }
3293        }
3294
3295        for(int j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
3296        {
3297            if(qinfo->RewRepFaction[j])
3298            {
3299                if(!qinfo->RewRepValue[j])
3300                {
3301                    sLog.outErrorDb("Quest %u has `RewRepFaction%d` = %u but `RewRepValue%d` = 0, quest will not reward this reputation.",
3302                        qinfo->GetQuestId(),j+1,qinfo->RewRepValue[j],j+1);
3303                    // no changes
3304                }
3305
3306                if(!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
3307                {
3308                    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.",
3309                        qinfo->GetQuestId(),j+1,qinfo->RewRepFaction[j] ,qinfo->RewRepFaction[j] );
3310                    qinfo->RewRepFaction[j] = 0;            // quest will not reward this
3311                }
3312            }
3313            else if(qinfo->RewRepValue[j]!=0)
3314            {
3315                sLog.outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %u.",
3316                    qinfo->GetQuestId(),j+1,j+1,qinfo->RewRepValue[j]);
3317                // no changes, quest ignore this data
3318            }
3319        }
3320
3321        if(qinfo->RewSpell)
3322        {
3323            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);
3324
3325            if(!spellInfo)
3326            {
3327                sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.",
3328                    qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
3329                qinfo->RewSpell = 0;                        // no spell reward will display for this quest
3330            }
3331
3332            else if(!SpellMgr::IsSpellValid(spellInfo))
3333            {
3334                sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest can't be done.",
3335                    qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
3336                qinfo->RewSpell = 0;                        // no spell reward will display for this quest
3337            }
3338
3339        }
3340
3341        if(qinfo->RewSpellCast)
3342        {
3343            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);
3344
3345            if(!spellInfo)
3346            {
3347                sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.",
3348                    qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
3349                qinfo->RewSpellCast = 0;                    // no spell will be casted on player
3350            }
3351
3352            else if(!SpellMgr::IsSpellValid(spellInfo))
3353            {
3354                sLog.outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest can't be done.",
3355                    qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
3356                qinfo->RewSpellCast = 0;                    // no spell will be casted on player
3357            }
3358
3359        }
3360
3361        if(qinfo->RewMailTemplateId)
3362        {
3363            if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
3364            {
3365                sLog.outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template  %u does not exist, quest will not have a mail reward.",
3366                    qinfo->GetQuestId(),qinfo->RewMailTemplateId,qinfo->RewMailTemplateId);
3367                qinfo->RewMailTemplateId = 0;               // no mail will send to player
3368                qinfo->RewMailDelaySecs = 0;                // no mail will send to player
3369            }
3370        }
3371
3372        if(qinfo->NextQuestInChain)
3373        {
3374            if(mQuestTemplates.find(qinfo->NextQuestInChain) == mQuestTemplates.end())
3375            {
3376                sLog.outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.",
3377                    qinfo->GetQuestId(),qinfo->NextQuestInChain ,qinfo->NextQuestInChain );
3378                qinfo->NextQuestInChain = 0;
3379            }
3380            else
3381                mQuestTemplates[qinfo->NextQuestInChain]->prevChainQuests.push_back(qinfo->GetQuestId());
3382        }
3383
3384        // fill additional data stores
3385        if(qinfo->PrevQuestId)
3386        {
3387            if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
3388            {
3389                sLog.outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
3390            }
3391            else
3392            {
3393                qinfo->prevQuests.push_back(qinfo->PrevQuestId);
3394            }
3395        }
3396
3397        if(qinfo->NextQuestId)
3398        {
3399            if (mQuestTemplates.find(abs(qinfo->GetNextQuestId())) == mQuestTemplates.end())
3400            {
3401                sLog.outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
3402            }
3403            else
3404            {
3405                int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
3406                mQuestTemplates[abs(qinfo->GetNextQuestId())]->prevQuests.push_back(signedQuestId);
3407            }
3408        }
3409
3410        if(qinfo->ExclusiveGroup)
3411            mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
3412        if(qinfo->LimitTime)
3413            qinfo->SetFlag(QUEST_TRINITY_FLAGS_TIMED);
3414    }
3415
3416    // check QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
3417    for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
3418    {
3419        SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
3420        if(!spellInfo)
3421            continue;
3422
3423        for(int j = 0; j < 3; ++j)
3424        {
3425            if(spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
3426                continue;
3427
3428            uint32 quest_id = spellInfo->EffectMiscValue[j];
3429
3430            Quest const* quest = GetQuestTemplate(quest_id);
3431
3432            // some quest referenced in spells not exist (outdataed spells)
3433            if(!quest)
3434                continue;
3435
3436            if(!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
3437            {
3438                sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.",spellInfo->Id,quest_id);
3439
3440                // this will prevent quest completing without objective
3441                const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
3442            }
3443        }
3444    }
3445
3446    sLog.outString();
3447    sLog.outString( ">> Loaded %u quests definitions", mQuestTemplates.size() );
3448}
3449
3450void ObjectMgr::LoadQuestLocales()
3451{
3452    mQuestLocaleMap.clear();
3453
3454    QueryResult *result = WorldDatabase.Query("SELECT entry,"
3455        "Title_loc1,Details_loc1,Objectives_loc1,OfferRewardText_loc1,RequestItemsText_loc1,EndText_loc1,ObjectiveText1_loc1,ObjectiveText2_loc1,ObjectiveText3_loc1,ObjectiveText4_loc1,"
3456        "Title_loc2,Details_loc2,Objectives_loc2,OfferRewardText_loc2,RequestItemsText_loc2,EndText_loc2,ObjectiveText1_loc2,ObjectiveText2_loc2,ObjectiveText3_loc2,ObjectiveText4_loc2,"
3457        "Title_loc3,Details_loc3,Objectives_loc3,OfferRewardText_loc3,RequestItemsText_loc3,EndText_loc3,ObjectiveText1_loc3,ObjectiveText2_loc3,ObjectiveText3_loc3,ObjectiveText4_loc3,"
3458        "Title_loc4,Details_loc4,Objectives_loc4,OfferRewardText_loc4,RequestItemsText_loc4,EndText_loc4,ObjectiveText1_loc4,ObjectiveText2_loc4,ObjectiveText3_loc4,ObjectiveText4_loc4,"
3459        "Title_loc5,Details_loc5,Objectives_loc5,OfferRewardText_loc5,RequestItemsText_loc5,EndText_loc5,ObjectiveText1_loc5,ObjectiveText2_loc5,ObjectiveText3_loc5,ObjectiveText4_loc5,"
3460        "Title_loc6,Details_loc6,Objectives_loc6,OfferRewardText_loc6,RequestItemsText_loc6,EndText_loc6,ObjectiveText1_loc6,ObjectiveText2_loc6,ObjectiveText3_loc6,ObjectiveText4_loc6,"
3461        "Title_loc7,Details_loc7,Objectives_loc7,OfferRewardText_loc7,RequestItemsText_loc7,EndText_loc7,ObjectiveText1_loc7,ObjectiveText2_loc7,ObjectiveText3_loc7,ObjectiveText4_loc7,"
3462        "Title_loc8,Details_loc8,Objectives_loc8,OfferRewardText_loc8,RequestItemsText_loc8,EndText_loc8,ObjectiveText1_loc8,ObjectiveText2_loc8,ObjectiveText3_loc8,ObjectiveText4_loc8"
3463        " FROM locales_quest"
3464        );
3465
3466    if(!result)
3467    {
3468        barGoLink bar(1);
3469
3470        bar.step();
3471
3472        sLog.outString("");
3473        sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_quest` is empty.");
3474        return;
3475    }
3476
3477    barGoLink bar(result->GetRowCount());
3478
3479    do
3480    {
3481        Field *fields = result->Fetch();
3482        bar.step();
3483
3484        uint32 entry = fields[0].GetUInt32();
3485
3486        QuestLocale& data = mQuestLocaleMap[entry];
3487
3488        for(int i = 1; i < MAX_LOCALE; ++i)
3489        {
3490            std::string str = fields[1+10*(i-1)].GetCppString();
3491            if(!str.empty())
3492            {
3493                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3494                if(idx >= 0)
3495                {
3496                    if(data.Title.size() <= idx)
3497                        data.Title.resize(idx+1);
3498
3499                    data.Title[idx] = str;
3500                }
3501            }
3502            str = fields[1+10*(i-1)+1].GetCppString();
3503            if(!str.empty())
3504            {
3505                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3506                if(idx >= 0)
3507                {
3508                    if(data.Details.size() <= idx)
3509                        data.Details.resize(idx+1);
3510
3511                    data.Details[idx] = str;
3512                }
3513            }
3514            str = fields[1+10*(i-1)+2].GetCppString();
3515            if(!str.empty())
3516            {
3517                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3518                if(idx >= 0)
3519                {
3520                    if(data.Objectives.size() <= idx)
3521                        data.Objectives.resize(idx+1);
3522
3523                    data.Objectives[idx] = str;
3524                }
3525            }
3526            str = fields[1+10*(i-1)+3].GetCppString();
3527            if(!str.empty())
3528            {
3529                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3530                if(idx >= 0)
3531                {
3532                    if(data.OfferRewardText.size() <= idx)
3533                        data.OfferRewardText.resize(idx+1);
3534
3535                    data.OfferRewardText[idx] = str;
3536                }
3537            }
3538            str = fields[1+10*(i-1)+4].GetCppString();
3539            if(!str.empty())
3540            {
3541                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3542                if(idx >= 0)
3543                {
3544                    if(data.RequestItemsText.size() <= idx)
3545                        data.RequestItemsText.resize(idx+1);
3546
3547                    data.RequestItemsText[idx] = str;
3548                }
3549            }
3550            str = fields[1+10*(i-1)+5].GetCppString();
3551            if(!str.empty())
3552            {
3553                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3554                if(idx >= 0)
3555                {
3556                    if(data.EndText.size() <= idx)
3557                        data.EndText.resize(idx+1);
3558
3559                    data.EndText[idx] = str;
3560                }
3561            }
3562            for(int k = 0; k < 4; ++k)
3563            {
3564                str = fields[1+10*(i-1)+6+k].GetCppString();
3565                if(!str.empty())
3566                {
3567                    int idx = GetOrNewIndexForLocale(LocaleConstant(i));
3568                    if(idx >= 0)
3569                    {
3570                        if(data.ObjectiveText[k].size() <= idx)
3571                            data.ObjectiveText[k].resize(idx+1);
3572
3573                        data.ObjectiveText[k][idx] = str;
3574                    }
3575                }
3576            }
3577        }
3578    } while (result->NextRow());
3579
3580    delete result;
3581
3582    sLog.outString();
3583    sLog.outString( ">> Loaded %u Quest locale strings", mQuestLocaleMap.size() );
3584}
3585
3586void ObjectMgr::LoadPetCreateSpells()
3587{
3588    QueryResult *result = WorldDatabase.PQuery("SELECT entry, Spell1, Spell2, Spell3, Spell4 FROM petcreateinfo_spell");
3589    if(!result)
3590    {
3591        barGoLink bar( 1 );
3592        bar.step();
3593
3594        sLog.outString();
3595        sLog.outString( ">> Loaded 0 pet create spells" );
3596        sLog.outErrorDb("`petcreateinfo_spell` table is empty!");
3597        return;
3598    }
3599
3600    uint32 count = 0;
3601
3602    barGoLink bar( result->GetRowCount() );
3603
3604    mPetCreateSpell.clear();
3605
3606    do
3607    {
3608        Field *fields = result->Fetch();
3609        bar.step();
3610
3611        uint32 creature_id = fields[0].GetUInt32();
3612
3613        if(!creature_id || !sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
3614            continue;
3615
3616        PetCreateSpellEntry PetCreateSpell;
3617        for(int i = 0; i < 4; i++)
3618        {
3619            PetCreateSpell.spellid[i] = fields[i + 1].GetUInt32();
3620
3621            if(PetCreateSpell.spellid[i] && !sSpellStore.LookupEntry(PetCreateSpell.spellid[i]))
3622                sLog.outErrorDb("Spell %u listed in `petcreateinfo_spell` does not exist",PetCreateSpell.spellid[i]);
3623        }
3624
3625        mPetCreateSpell[creature_id] = PetCreateSpell;
3626
3627        ++count;
3628    }
3629    while (result->NextRow());
3630
3631    delete result;
3632
3633    sLog.outString();
3634    sLog.outString( ">> Loaded %u pet create spells", count );
3635}
3636
3637void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
3638{
3639    if(sWorld.IsScriptScheduled())                          // function don't must be called in time scripts use.
3640        return;
3641
3642    sLog.outString( "%s :", tablename);
3643
3644    scripts.clear();                                        // need for reload support
3645
3646    QueryResult *result = WorldDatabase.PQuery( "SELECT id,delay,command,datalong,datalong2,datatext, x, y, z, o FROM %s", tablename );
3647
3648    uint32 count = 0;
3649
3650    if( !result )
3651    {
3652        barGoLink bar( 1 );
3653        bar.step();
3654
3655        sLog.outString();
3656        sLog.outString( ">> Loaded %u script definitions", count );
3657        return;
3658    }
3659
3660    barGoLink bar( result->GetRowCount() );
3661
3662    do
3663    {
3664        bar.step();
3665
3666        Field *fields = result->Fetch();
3667        ScriptInfo tmp;
3668        tmp.id = fields[0].GetUInt32();
3669        tmp.delay = fields[1].GetUInt32();
3670        tmp.command = fields[2].GetUInt32();
3671        tmp.datalong = fields[3].GetUInt32();
3672        tmp.datalong2 = fields[4].GetUInt32();
3673        tmp.datatext = fields[5].GetCppString();
3674        tmp.x = fields[6].GetFloat();
3675        tmp.y = fields[7].GetFloat();
3676        tmp.z = fields[8].GetFloat();
3677        tmp.o = fields[9].GetFloat();
3678
3679        // generic command args check
3680        switch(tmp.command)
3681        {
3682            case SCRIPT_COMMAND_TALK:
3683            {
3684                if(tmp.datalong > 3)
3685                {
3686                    sLog.outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.datalong,tmp.id);
3687                    continue;
3688                }
3689                break;
3690            }
3691
3692            case SCRIPT_COMMAND_TELEPORT_TO:
3693            {
3694                if(!sMapStore.LookupEntry(tmp.datalong))
3695                {
3696                    sLog.outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u",tablename,tmp.datalong,tmp.id);
3697                    continue;
3698                }
3699
3700                if(!Trinity::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
3701                {
3702                    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);
3703                    continue;
3704                }
3705                break;
3706            }
3707
3708            case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
3709            {
3710                if(!Trinity::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
3711                {
3712                    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);
3713                    continue;
3714                }
3715
3716                if(!GetCreatureTemplate(tmp.datalong))
3717                {
3718                    sLog.outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u",tablename,tmp.datalong,tmp.id);
3719                    continue;
3720                }
3721                break;
3722            }
3723
3724            case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
3725            {
3726                GameObjectData const* data = GetGOData(tmp.datalong);
3727                if(!data)
3728                {
3729                    sLog.outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,tmp.datalong,tmp.id);
3730                    continue;
3731                }
3732
3733                GameObjectInfo const* info = GetGameObjectInfo(data->id);
3734                if(!info)
3735                {
3736                    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);
3737                    continue;
3738                }
3739
3740                if( info->type==GAMEOBJECT_TYPE_FISHINGNODE ||
3741                    info->type==GAMEOBJECT_TYPE_FISHINGHOLE ||
3742                    info->type==GAMEOBJECT_TYPE_DOOR        ||
3743                    info->type==GAMEOBJECT_TYPE_BUTTON      ||
3744                    info->type==GAMEOBJECT_TYPE_TRAP )
3745                {
3746                    sLog.outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u",tablename,info->id,tmp.id);
3747                    continue;
3748                }
3749                break;
3750            }
3751            case SCRIPT_COMMAND_OPEN_DOOR:
3752            case SCRIPT_COMMAND_CLOSE_DOOR:
3753            {
3754                GameObjectData const* data = GetGOData(tmp.datalong);
3755                if(!data)
3756                {
3757                    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);
3758                    continue;
3759                }
3760
3761                GameObjectInfo const* info = GetGameObjectInfo(data->id);
3762                if(!info)
3763                {
3764                    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);
3765                    continue;
3766                }
3767
3768                if( info->type!=GAMEOBJECT_TYPE_DOOR)
3769                {
3770                    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);
3771                    continue;
3772                }
3773
3774                break;
3775            }
3776            case SCRIPT_COMMAND_QUEST_EXPLORED:
3777            {
3778                Quest const* quest = GetQuestTemplate(tmp.datalong);
3779                if(!quest)
3780                {
3781                    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);
3782                    continue;
3783                }
3784
3785                if(!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
3786                {
3787                    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_TRINITY_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",tablename,tmp.datalong,tmp.id);
3788
3789                    // this will prevent quest completing without objective
3790                    const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
3791
3792                    // continue; - quest objective requiremet set and command can be allowed
3793                }
3794
3795                if(float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
3796                {
3797                    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",tablename,tmp.datalong2,tmp.id);
3798                    continue;
3799                }
3800
3801                if(tmp.datalong2 && float(tmp.datalong2) > DEFAULT_VISIBILITY_DISTANCE)
3802                {
3803                    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 %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(DEFAULT_VISIBILITY_DISTANCE));
3804                    continue;
3805                }
3806
3807                if(tmp.datalong2 && float(tmp.datalong2) < INTERACTION_DISTANCE)
3808                {
3809                    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 %u or 0 for disable distance check",tablename,tmp.datalong2,tmp.id,uint32(INTERACTION_DISTANCE));
3810                    continue;
3811                }
3812
3813                break;
3814            }
3815
3816            case SCRIPT_COMMAND_REMOVE_AURA:
3817            case SCRIPT_COMMAND_CAST_SPELL:
3818            {
3819                if(!sSpellStore.LookupEntry(tmp.datalong))
3820                {
3821                    sLog.outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA or SCRIPT_COMMAND_CAST_SPELL for script id %u",tablename,tmp.datalong,tmp.id);
3822                    continue;
3823                }
3824                break;
3825            }
3826        }
3827
3828        if (scripts.find(tmp.id) == scripts.end())
3829        {
3830            ScriptMap emptyMap;
3831            scripts[tmp.id] = emptyMap;
3832        }
3833        scripts[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
3834
3835        ++count;
3836    } while( result->NextRow() );
3837
3838    delete result;
3839
3840    sLog.outString();
3841    sLog.outString( ">> Loaded %u script definitions", count );
3842}
3843
3844void ObjectMgr::LoadGameObjectScripts()
3845{
3846    LoadScripts(sGameObjectScripts,    "gameobject_scripts");
3847
3848    // check ids
3849    for(ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
3850    {
3851        if(!GetGOData(itr->first))
3852            sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id",itr->first);
3853    }
3854}
3855
3856void ObjectMgr::LoadQuestEndScripts()
3857{
3858    LoadScripts(sQuestEndScripts,  "quest_end_scripts");
3859
3860    // check ids
3861    for(ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
3862    {
3863        if(!GetQuestTemplate(itr->first))
3864            sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id",itr->first);
3865    }
3866}
3867
3868void ObjectMgr::LoadQuestStartScripts()
3869{
3870    LoadScripts(sQuestStartScripts,"quest_start_scripts");
3871
3872    // check ids
3873    for(ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
3874    {
3875        if(!GetQuestTemplate(itr->first))
3876            sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id",itr->first);
3877    }
3878}
3879
3880void ObjectMgr::LoadSpellScripts()
3881{
3882    LoadScripts(sSpellScripts, "spell_scripts");
3883
3884    // check ids
3885    for(ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
3886    {
3887        SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
3888
3889        if(!spellInfo)
3890        {
3891            sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id",itr->first);
3892            continue;
3893        }
3894
3895        //check for correct spellEffect
3896        bool found = false;
3897        for(int i=0; i<3; ++i)
3898        {
3899            // skip empty effects
3900            if( !spellInfo->Effect[i] )
3901                continue;
3902
3903            if( spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT )
3904            {
3905                found =  true;
3906                break;
3907            }
3908        }
3909
3910        if(!found)
3911            sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect",itr->first,SPELL_EFFECT_SCRIPT_EFFECT);
3912    }
3913}
3914
3915void ObjectMgr::LoadEventScripts()
3916{
3917    LoadScripts(sEventScripts, "event_scripts");
3918
3919    std::set<uint32> evt_scripts;
3920    // Load all possible script entries from gameobjects
3921    for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
3922    {
3923        GameObjectInfo const * goInfo = sGOStorage.LookupEntry<GameObjectInfo>(i);
3924        if (goInfo)
3925        {
3926            switch(goInfo->type)
3927            {
3928                case GAMEOBJECT_TYPE_GOOBER:
3929                    if(goInfo->goober.eventId)
3930                        evt_scripts.insert(goInfo->goober.eventId);
3931                    break;
3932                case GAMEOBJECT_TYPE_CHEST:
3933                    if(goInfo->chest.eventId)
3934                        evt_scripts.insert(goInfo->chest.eventId);
3935                    break;
3936                default:
3937                    break;
3938            }
3939        }
3940    }
3941    // Load all possible script entries from spells
3942    for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
3943    {
3944        SpellEntry const * spell = sSpellStore.LookupEntry(i);
3945        if (spell)
3946        {
3947            for(int j=0; j<3; ++j)
3948            {
3949                if( spell->Effect[j] == SPELL_EFFECT_SEND_EVENT )
3950                {
3951                    if (spell->EffectMiscValue[j])
3952                        evt_scripts.insert(spell->EffectMiscValue[j]);
3953                }
3954            }
3955        }
3956    }
3957    // Then check if all scripts are in above list of possible script entries
3958    for(ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
3959    {
3960        std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
3961        if (itr2 == evt_scripts.end())
3962            sLog.outErrorDb("Table `event_scripts` has script (Id: %u) not refering to any gameobject_template type 10 data2 field or type 3 data6 field or any spell effect %u", itr->first, SPELL_EFFECT_SEND_EVENT);
3963    }
3964}
3965
3966void ObjectMgr::LoadItemTexts()
3967{
3968    QueryResult *result = CharacterDatabase.PQuery("SELECT id, text FROM item_text");
3969
3970    uint32 count = 0;
3971
3972    if( !result )
3973    {
3974        barGoLink bar( 1 );
3975        bar.step();
3976
3977        sLog.outString();
3978        sLog.outString( ">> Loaded %u item pages", count );
3979        return;
3980    }
3981
3982    barGoLink bar( result->GetRowCount() );
3983
3984    Field* fields;
3985    do
3986    {
3987        bar.step();
3988
3989        fields = result->Fetch();
3990
3991        mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
3992
3993        ++count;
3994
3995    } while ( result->NextRow() );
3996
3997    delete result;
3998
3999    sLog.outString();
4000    sLog.outString( ">> Loaded %u item texts", count );
4001}
4002
4003void ObjectMgr::LoadPageTexts()
4004{
4005    sPageTextStore.Free();                                  // for reload case
4006
4007    sPageTextStore.Load();
4008    sLog.outString( ">> Loaded %u page texts", sPageTextStore.RecordCount );
4009    sLog.outString();
4010
4011    for(uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
4012    {
4013        // check data correctness
4014        PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
4015        if(!page)
4016            continue;
4017
4018        if(page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
4019        {
4020            sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i,page->Next_Page);
4021            continue;
4022        }
4023
4024        // detect circular reference
4025        std::set<uint32> checkedPages;
4026        for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
4027        {
4028            if(!pageItr->Next_Page)
4029                break;
4030            checkedPages.insert(pageItr->Page_ID);
4031            if(checkedPages.find(pageItr->Next_Page)!=checkedPages.end())
4032            {
4033                std::ostringstream ss;
4034                ss<< "The text page(s) ";
4035                for (std::set<uint32>::iterator itr= checkedPages.begin();itr!=checkedPages.end(); itr++)
4036                    ss << *itr << " ";
4037                ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page "
4038                    << pageItr->Page_ID <<" to 0";
4039                sLog.outErrorDb(ss.str().c_str());
4040                const_cast<PageText*>(pageItr)->Next_Page = 0;
4041                break;
4042            }
4043        }
4044    }
4045}
4046
4047void ObjectMgr::LoadPageTextLocales()
4048{
4049    mPageTextLocaleMap.clear();
4050   
4051    QueryResult *result = WorldDatabase.PQuery("SELECT entry,text_loc1,text_loc2,text_loc3,text_loc4,text_loc5,text_loc6,text_loc7,text_loc8 FROM locales_page_text");
4052
4053    if(!result)
4054    {
4055        barGoLink bar(1);
4056
4057        bar.step();
4058
4059        sLog.outString("");
4060        sLog.outString(">> Loaded 0 PageText locale strings. DB table `locales_page_text` is empty.");
4061        return;
4062    }
4063
4064    barGoLink bar(result->GetRowCount());
4065
4066    do
4067    {
4068        Field *fields = result->Fetch();
4069        bar.step();
4070
4071        uint32 entry = fields[0].GetUInt32();
4072
4073        PageTextLocale& data = mPageTextLocaleMap[entry];
4074
4075        for(int i = 1; i < MAX_LOCALE; ++i)
4076        {
4077            std::string str = fields[i].GetCppString();
4078            if(str.empty())
4079                continue;
4080
4081            int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4082            if(idx >= 0)
4083            {
4084                if(data.Text.size() <= idx)
4085                    data.Text.resize(idx+1);
4086
4087                data.Text[idx] = str;
4088            }
4089        }
4090
4091    } while (result->NextRow());
4092
4093    delete result;
4094
4095    sLog.outString();
4096    sLog.outString( ">> Loaded %u PageText locale strings", mPageTextLocaleMap.size() );
4097}
4098
4099void ObjectMgr::LoadInstanceTemplate()
4100{
4101    sInstanceTemplate.Load();
4102
4103    for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
4104    {
4105        InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
4106        if(!temp) continue;
4107        const MapEntry* entry = sMapStore.LookupEntry(temp->map);
4108        if(!entry)
4109        {
4110            sLog.outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);
4111            continue;
4112        }
4113        else if(!entry->HasResetTime())
4114            continue;
4115
4116        if(temp->reset_delay == 0)
4117        {
4118            // use defaults from the DBC
4119            if(entry->SupportsHeroicMode())
4120            {
4121                temp->reset_delay = entry->resetTimeHeroic / DAY;
4122            }
4123            else if (entry->resetTimeRaid && entry->map_type == MAP_RAID)
4124            {
4125                temp->reset_delay = entry->resetTimeRaid / DAY;
4126            }
4127        }
4128
4129        // the reset_delay must be atleast one day
4130        temp->reset_delay = std::max((uint32)1, (uint32)(temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME)));
4131    }
4132
4133    sLog.outString( ">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount );
4134    sLog.outString();
4135}
4136
4137void ObjectMgr::AddGossipText(GossipText *pGText)
4138{
4139    ASSERT( pGText->Text_ID );
4140    ASSERT( mGossipText.find(pGText->Text_ID) == mGossipText.end() );
4141    mGossipText[pGText->Text_ID] = pGText;
4142}
4143
4144GossipText *ObjectMgr::GetGossipText(uint32 Text_ID)
4145{
4146    GossipTextMap::const_iterator itr;
4147    for (itr = mGossipText.begin(); itr != mGossipText.end(); itr++)
4148    {
4149        if(itr->second->Text_ID == Text_ID)
4150            return itr->second;
4151    }
4152    return NULL;
4153}
4154
4155void ObjectMgr::LoadGossipText()
4156{
4157    GossipText *pGText;
4158    QueryResult *result = WorldDatabase.Query( "SELECT * FROM npc_text" );
4159
4160    int count = 0;
4161    if( !result )
4162    {
4163        barGoLink bar( 1 );
4164        bar.step();
4165
4166        sLog.outString();
4167        sLog.outString( ">> Loaded %u npc texts", count );
4168        return;
4169    }
4170
4171    int cic;
4172
4173    barGoLink bar( result->GetRowCount() );
4174
4175    do
4176    {
4177        ++count;
4178        cic = 0;
4179
4180        Field *fields = result->Fetch();
4181
4182        bar.step();
4183
4184        pGText = new GossipText;
4185        pGText->Text_ID    = fields[cic++].GetUInt32();
4186
4187        for (int i=0; i< 8; i++)
4188        {
4189            pGText->Options[i].Text_0           = fields[cic++].GetCppString();
4190            pGText->Options[i].Text_1           = fields[cic++].GetCppString();
4191
4192            pGText->Options[i].Language         = fields[cic++].GetUInt32();
4193            pGText->Options[i].Probability      = fields[cic++].GetFloat();
4194
4195            pGText->Options[i].Emotes[0]._Delay  = fields[cic++].GetUInt32();
4196            pGText->Options[i].Emotes[0]._Emote  = fields[cic++].GetUInt32();
4197
4198            pGText->Options[i].Emotes[1]._Delay  = fields[cic++].GetUInt32();
4199            pGText->Options[i].Emotes[1]._Emote  = fields[cic++].GetUInt32();
4200
4201            pGText->Options[i].Emotes[2]._Delay  = fields[cic++].GetUInt32();
4202            pGText->Options[i].Emotes[2]._Emote  = fields[cic++].GetUInt32();
4203        }
4204
4205        if ( !pGText->Text_ID ) continue;
4206        AddGossipText( pGText );
4207
4208    } while( result->NextRow() );
4209
4210    sLog.outString();
4211    sLog.outString( ">> Loaded %u npc texts", count );
4212    delete result;
4213}
4214
4215void ObjectMgr::LoadNpcTextLocales()
4216{
4217    mNpcTextLocaleMap.clear();
4218   
4219    QueryResult *result = WorldDatabase.Query("SELECT entry,"
4220        "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,"
4221        "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,"
4222        "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,"
4223        "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,"
4224        "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,"
4225        "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,"
4226        "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, "
4227        "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 "
4228        " FROM locales_npc_text");
4229
4230    if(!result)
4231    {
4232        barGoLink bar(1);
4233
4234        bar.step();
4235
4236        sLog.outString("");
4237        sLog.outString(">> Loaded 0 Quest locale strings. DB table `locales_npc_text` is empty.");
4238        return;
4239    }
4240
4241    barGoLink bar(result->GetRowCount());
4242
4243    do
4244    {
4245        Field *fields = result->Fetch();
4246        bar.step();
4247
4248        uint32 entry = fields[0].GetUInt32();
4249
4250        NpcTextLocale& data = mNpcTextLocaleMap[entry];
4251
4252        for(int i=1; i<MAX_LOCALE; ++i)
4253        {
4254            for(int j=0; j<8; ++j)
4255            {
4256                std::string str0 = fields[1+8*2*(i-1)+2*j].GetCppString();
4257                if(!str0.empty())
4258                {
4259                    int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4260                    if(idx >= 0)
4261                    {
4262                        if(data.Text_0[j].size() <= idx)
4263                            data.Text_0[j].resize(idx+1);
4264
4265                        data.Text_0[j][idx] = str0;
4266                    }
4267                }
4268                std::string str1 = fields[1+8*2*(i-1)+2*j+1].GetCppString();
4269                if(!str1.empty())
4270                {
4271                    int idx = GetOrNewIndexForLocale(LocaleConstant(i));
4272                    if(idx >= 0)
4273                    {
4274                        if(data.Text_1[j].size() <= idx)
4275                            data.Text_1[j].resize(idx+1);
4276
4277                        data.Text_1[j][idx] = str1;
4278                    }
4279                }
4280            }
4281        }
4282    } while (result->NextRow());
4283
4284    delete result;
4285
4286    sLog.outString();
4287    sLog.outString( ">> Loaded %u NpcText locale strings", mNpcTextLocaleMap.size() );
4288}
4289
4290//not very fast function but it is called only once a day, or on starting-up
4291void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
4292{
4293    time_t basetime = time(NULL);
4294    sLog.outDebug("Returning mails current time: hour: %d, minute: %d, second: %d ", localtime(&basetime)->tm_hour, localtime(&basetime)->tm_min, localtime(&basetime)->tm_sec);
4295    //delete all old mails without item and without body immediately, if starting server
4296    if (!serverUp)
4297        CharacterDatabase.PExecute("DELETE FROM mail WHERE expire_time < '" I64FMTD "' AND has_items = '0' AND itemTextId = 0", (uint64)basetime);
4298    //                                                     0  1           2      3        4          5         6           7   8       9
4299    QueryResult* result = CharacterDatabase.PQuery("SELECT id,messageType,sender,receiver,itemTextId,has_items,expire_time,cod,checked,mailTemplateId FROM mail WHERE expire_time < '" I64FMTD "'", (uint64)basetime);
4300    if ( !result )
4301        return;                                             // any mails need to be returned or deleted
4302    Field *fields;
4303    //std::ostringstream delitems, delmails; //will be here for optimization
4304    //bool deletemail = false, deleteitem = false;
4305    //delitems << "DELETE FROM item_instance WHERE guid IN ( ";
4306    //delmails << "DELETE FROM mail WHERE id IN ( "
4307    do
4308    {
4309        fields = result->Fetch();
4310        Mail *m = new Mail;
4311        m->messageID = fields[0].GetUInt32();
4312        m->messageType = fields[1].GetUInt8();
4313        m->sender = fields[2].GetUInt32();
4314        m->receiver = fields[3].GetUInt32();
4315        m->itemTextId = fields[4].GetUInt32();
4316        bool has_items = fields[5].GetBool();
4317        m->expire_time = (time_t)fields[6].GetUInt64();
4318        m->deliver_time = 0;
4319        m->COD = fields[7].GetUInt32();
4320        m->checked = fields[8].GetUInt32();
4321        m->mailTemplateId = fields[9].GetInt16();
4322
4323        Player *pl = 0;
4324        if (serverUp)
4325            pl = GetPlayer((uint64)m->receiver);
4326        if (pl && pl->m_mailsLoaded)
4327        {                                                   //this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
4328            //his in mailbox and he has already listed his mails )
4329            delete m;
4330            continue;
4331        }
4332        //delete or return mail:
4333        if (has_items)
4334        {
4335            QueryResult *resultItems = CharacterDatabase.PQuery("SELECT item_guid,item_template FROM mail_items WHERE mail_id='%u'", m->messageID);
4336            if(resultItems)
4337            {
4338                do
4339                {
4340                    Field *fields2 = resultItems->Fetch();
4341
4342                    uint32 item_guid_low = fields2[0].GetUInt32();
4343                    uint32 item_template = fields2[1].GetUInt32();
4344
4345                    m->AddItem(item_guid_low, item_template);
4346                }
4347                while (resultItems->NextRow());
4348
4349                delete resultItems;
4350            }
4351            //if it is mail from AH, it shouldn't be returned, but deleted
4352            if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_AUCTION | MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
4353            {
4354                // mail open and then not returned
4355                for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
4356                    CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", itr2->item_guid);
4357            }
4358            else
4359            {
4360                //mail will be returned:
4361                CharacterDatabase.PExecute("UPDATE mail SET sender = '%u', receiver = '%u', expire_time = '" I64FMTD "', deliver_time = '" I64FMTD "',cod = '0', checked = '%u' WHERE id = '%u'", m->receiver, m->sender, (uint64)(basetime + 30*DAY), (uint64)basetime, MAIL_CHECK_MASK_RETURNED, m->messageID);
4362                delete m;
4363                continue;
4364            }
4365        }
4366
4367        if (m->itemTextId)
4368            CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", m->itemTextId);
4369
4370        //deletemail = true;
4371        //delmails << m->messageID << ", ";
4372        CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", m->messageID);
4373        delete m;
4374    } while (result->NextRow());
4375    delete result;
4376}
4377
4378void ObjectMgr::LoadQuestAreaTriggers()
4379{
4380    mQuestAreaTriggerMap.clear();                           // need for reload case
4381
4382    QueryResult *result = WorldDatabase.Query( "SELECT id,quest FROM areatrigger_involvedrelation" );
4383
4384    uint32 count = 0;
4385
4386    if( !result )
4387    {
4388        barGoLink bar( 1 );
4389        bar.step();
4390
4391        sLog.outString();
4392        sLog.outString( ">> Loaded %u quest trigger points", count );
4393        return;
4394    }
4395
4396    barGoLink bar( result->GetRowCount() );
4397
4398    do
4399    {
4400        ++count;
4401        bar.step();
4402
4403        Field *fields = result->Fetch();
4404
4405        uint32 trigger_ID = fields[0].GetUInt32();
4406        uint32 quest_ID   = fields[1].GetUInt32();
4407
4408        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
4409        if(!atEntry)
4410        {
4411            sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",trigger_ID);
4412            continue;
4413        }
4414
4415        Quest const* quest = GetQuestTemplate(quest_ID);
4416
4417        if(!quest)
4418        {
4419            sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u",trigger_ID,quest_ID);
4420            continue;
4421        }
4422
4423        if(!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
4424        {
4425            sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.",trigger_ID,quest_ID);
4426
4427            // this will prevent quest completing without objective
4428            const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
4429
4430            // continue; - quest modified to required obkective and trigger can be allowed.
4431        }
4432
4433        mQuestAreaTriggerMap[trigger_ID] = quest_ID;
4434
4435    } while( result->NextRow() );
4436
4437    delete result;
4438
4439    sLog.outString();
4440    sLog.outString( ">> Loaded %u quest trigger points", count );
4441}
4442
4443void ObjectMgr::LoadTavernAreaTriggers()
4444{
4445    mTavernAreaTriggerSet.clear();                          // need for reload case
4446
4447    QueryResult *result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");
4448
4449    uint32 count = 0;
4450
4451    if( !result )
4452    {
4453        barGoLink bar( 1 );
4454        bar.step();
4455
4456        sLog.outString();
4457        sLog.outString( ">> Loaded %u tavern triggers", count );
4458        return;
4459    }
4460
4461    barGoLink bar( result->GetRowCount() );
4462
4463    do
4464    {
4465        ++count;
4466        bar.step();
4467
4468        Field *fields = result->Fetch();
4469
4470        uint32 Trigger_ID      = fields[0].GetUInt32();
4471
4472        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4473        if(!atEntry)
4474        {
4475            sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
4476            continue;
4477        }
4478
4479        mTavernAreaTriggerSet.insert(Trigger_ID);
4480    } while( result->NextRow() );
4481
4482    delete result;
4483
4484    sLog.outString();
4485    sLog.outString( ">> Loaded %u tavern triggers", count );
4486}
4487
4488void ObjectMgr::LoadAreaTriggerScripts()
4489{
4490    mAreaTriggerScripts.clear();                            // need for reload case
4491    QueryResult *result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");
4492
4493    uint32 count = 0;
4494
4495    if( !result )
4496    {
4497        barGoLink bar( 1 );
4498        bar.step();
4499
4500        sLog.outString();
4501        sLog.outString( ">> Loaded %u areatrigger scripts", count );
4502        return;
4503    }
4504
4505    barGoLink bar( result->GetRowCount() );
4506
4507    do
4508    {
4509        ++count;
4510        bar.step();
4511
4512        Field *fields = result->Fetch();
4513
4514        uint32 Trigger_ID      = fields[0].GetUInt32();
4515        std::string scriptName = fields[1].GetCppString();
4516
4517        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4518        if(!atEntry)
4519        {
4520            sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
4521            continue;
4522        }
4523        mAreaTriggerScripts[Trigger_ID] = scriptName;
4524    } while( result->NextRow() );
4525
4526    delete result;
4527
4528    sLog.outString();
4529    sLog.outString( ">> Loaded %u areatrigger scripts", count );
4530}
4531uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid )
4532{
4533    bool found = false;
4534    float dist;
4535    uint32 id = 0;
4536
4537    for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
4538    {
4539        TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
4540        if(node && node->map_id == mapid)
4541        {
4542            float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
4543            if(found)
4544            {
4545                if(dist2 < dist)
4546                {
4547                    dist = dist2;
4548                    id = i;
4549                }
4550            }
4551            else
4552            {
4553                found = true;
4554                dist = dist2;
4555                id = i;
4556            }
4557        }
4558    }
4559
4560    return id;
4561}
4562
4563void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost)
4564{
4565    TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
4566    if(src_i==sTaxiPathSetBySource.end())
4567    {
4568        path = 0;
4569        cost = 0;
4570        return;
4571    }
4572
4573    TaxiPathSetForSource& pathSet = src_i->second;
4574
4575    TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
4576    if(dest_i==pathSet.end())
4577    {
4578        path = 0;
4579        cost = 0;
4580        return;
4581    }
4582
4583    cost = dest_i->second.price;
4584    path = dest_i->second.ID;
4585}
4586
4587uint16 ObjectMgr::GetTaxiMount( uint32 id, uint32 team )
4588{
4589    uint16 mount_entry = 0;
4590    uint16 mount_id = 0;
4591
4592    TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
4593    if(node)
4594    {
4595        if (team == ALLIANCE)
4596        {
4597            mount_entry = node->alliance_mount_type;
4598            CreatureInfo const *ci = GetCreatureTemplate(mount_entry);
4599            if(ci)
4600                mount_id = ci->DisplayID_A;
4601        }
4602        if (team == HORDE)
4603        {
4604            mount_entry = node->horde_mount_type;
4605            CreatureInfo const *ci = GetCreatureTemplate(mount_entry);
4606            if(ci)
4607                mount_id = ci->DisplayID_H;
4608        }
4609    }
4610
4611    CreatureModelInfo const *minfo = GetCreatureModelInfo(mount_id);
4612    if(!minfo)
4613    {
4614        sLog.outErrorDb("Taxi mount (Entry: %u) for taxi node (Id: %u) for team %u has model %u not found in table `creature_model_info`, can't load. ",
4615            mount_entry,id,team,mount_id);
4616
4617        return false;
4618    }
4619    if(minfo->modelid_other_gender!=0)
4620        mount_id = urand(0,1) ? mount_id : minfo->modelid_other_gender;
4621
4622    return mount_id;
4623}
4624
4625void ObjectMgr::GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds)
4626{
4627    if(path >= sTaxiPathNodesByPath.size())
4628        return;
4629
4630    TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
4631
4632    pathnodes.Resize(nodeList.size());
4633    mapIds.resize(nodeList.size());
4634
4635    for(size_t i = 0; i < nodeList.size(); ++i)
4636    {
4637        pathnodes[ i ].x = nodeList[i].x;
4638        pathnodes[ i ].y = nodeList[i].y;
4639        pathnodes[ i ].z = nodeList[i].z;
4640
4641        mapIds[i] = nodeList[i].mapid;
4642    }
4643}
4644
4645void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
4646{
4647    if(path >= sTaxiPathNodesByPath.size())
4648        return;
4649
4650    TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
4651
4652    pathnodes.Resize(nodeList.size());
4653
4654    for(size_t i = 0; i < nodeList.size(); ++i)
4655    {
4656        pathnodes[ i ].mapid = nodeList[i].mapid;
4657        pathnodes[ i ].x = nodeList[i].x;
4658        pathnodes[ i ].y = nodeList[i].y;
4659        pathnodes[ i ].z = nodeList[i].z;
4660        pathnodes[ i ].actionFlag = nodeList[i].actionFlag;
4661        pathnodes[ i ].delay = nodeList[i].delay;
4662    }
4663}
4664
4665void ObjectMgr::LoadGraveyardZones()
4666{
4667    mGraveYardMap.clear();                                  // need for reload case
4668
4669    QueryResult *result = WorldDatabase.Query("SELECT id,ghost_zone,faction FROM game_graveyard_zone");
4670
4671    uint32 count = 0;
4672
4673    if( !result )
4674    {
4675        barGoLink bar( 1 );
4676        bar.step();
4677
4678        sLog.outString();
4679        sLog.outString( ">> Loaded %u graveyard-zone links", count );
4680        return;
4681    }
4682
4683    barGoLink bar( result->GetRowCount() );
4684
4685    do
4686    {
4687        ++count;
4688        bar.step();
4689
4690        Field *fields = result->Fetch();
4691
4692        uint32 safeLocId = fields[0].GetUInt32();
4693        uint32 zoneId = fields[1].GetUInt32();
4694        uint32 team   = fields[2].GetUInt32();
4695
4696        WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
4697        if(!entry)
4698        {
4699            sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",safeLocId);
4700            continue;
4701        }
4702
4703        AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
4704        if(!areaEntry)
4705        {
4706            sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.",zoneId);
4707            continue;
4708        }
4709
4710        if(areaEntry->zone != 0)
4711        {
4712            sLog.outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.",zoneId);
4713            continue;
4714        }
4715
4716        if(team!=0 && team!=HORDE && team!=ALLIANCE)
4717        {
4718            sLog.outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.",team);
4719            continue;
4720        }
4721
4722        if(entry->map_id != areaEntry->mapid && team != 0)
4723        {
4724            sLog.outErrorDb("Table `game_graveyard_zone` has record for ghost zone (%u) at map %u and graveyard (%u) at map %u for team %u, but in case maps are different, player faction setting is ignored. Use faction 0 instead.",zoneId,areaEntry->mapid, safeLocId, entry->map_id, team);
4725            team = 0;
4726        }
4727
4728        if(!AddGraveYardLink(safeLocId,zoneId,team,false))
4729            sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Garveyard (ID: %u) and Zone (ID: %u), skipped.",safeLocId,zoneId);
4730    } while( result->NextRow() );
4731
4732    delete result;
4733
4734    sLog.outString();
4735    sLog.outString( ">> Loaded %u graveyard-zone links", count );
4736}
4737
4738WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float z, uint32 MapId, uint32 team)
4739{
4740    // search for zone associated closest graveyard
4741    uint32 zoneId = MapManager::Instance().GetZoneId(MapId,x,y);
4742
4743    // Simulate std. algorithm:
4744    //   found some graveyard associated to (ghost_zone,ghost_map)
4745    //
4746    //   if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
4747    //     then check faction
4748    //   if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
4749    //     then skip check faction
4750    GraveYardMap::const_iterator graveLow  = mGraveYardMap.lower_bound(zoneId);
4751    GraveYardMap::const_iterator graveUp   = mGraveYardMap.upper_bound(zoneId);
4752    if(graveLow==graveUp)
4753    {
4754        sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
4755        return NULL;
4756    }
4757
4758    bool foundNear = false;
4759    float distNear;
4760    WorldSafeLocsEntry const* entryNear = NULL;
4761    WorldSafeLocsEntry const* entryFar = NULL;
4762
4763    for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
4764    {
4765        GraveYardData const& data = itr->second;
4766
4767        WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
4768        if(!entry)
4769        {
4770            sLog.outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.",data.safeLocId);
4771            continue;
4772        }
4773
4774        // remember first graveyard at another map and ignore other
4775        if(MapId != entry->map_id)
4776        {
4777            if(!entryFar)
4778                entryFar = entry;
4779            continue;
4780        }
4781
4782        // skip enemy faction graveyard at same map (normal area, city, or battleground)
4783        // team == 0 case can be at call from .neargrave
4784        if(data.team != 0 && team != 0 && data.team != team)
4785            continue;
4786
4787        // find now nearest graveyard at same map
4788        float dist2 = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y)+(entry->z - z)*(entry->z - z);
4789        if(foundNear)
4790        {
4791            if(dist2 < distNear)
4792            {
4793                distNear = dist2;
4794                entryNear = entry;
4795            }
4796        }
4797        else
4798        {
4799            foundNear = true;
4800            distNear = dist2;
4801            entryNear = entry;
4802        }
4803    }
4804
4805    if(entryNear)
4806        return entryNear;
4807
4808    return entryFar;
4809}
4810
4811GraveYardData const* ObjectMgr::FindGraveYardData(uint32 id, uint32 zoneId)
4812{
4813    GraveYardMap::const_iterator graveLow  = mGraveYardMap.lower_bound(zoneId);
4814    GraveYardMap::const_iterator graveUp   = mGraveYardMap.upper_bound(zoneId);
4815
4816    for(GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
4817    {
4818        if(itr->second.safeLocId==id)
4819            return &itr->second;
4820    }
4821
4822    return NULL;
4823}
4824
4825bool ObjectMgr::AddGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
4826{
4827    if(FindGraveYardData(id,zoneId))
4828        return false;
4829
4830    // add link to loaded data
4831    GraveYardData data;
4832    data.safeLocId = id;
4833    data.team = team;
4834
4835    mGraveYardMap.insert(GraveYardMap::value_type(zoneId,data));
4836
4837    // add link to DB
4838    if(inDB)
4839    {
4840        WorldDatabase.PExecuteLog("INSERT INTO game_graveyard_zone ( id,ghost_zone,faction) "
4841            "VALUES ('%u', '%u','%u')",id,zoneId,team);
4842    }
4843
4844    return true;
4845}
4846
4847void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
4848{
4849    GraveYardMap::iterator graveLow  = mGraveYardMap.lower_bound(zoneId);
4850    GraveYardMap::iterator graveUp   = mGraveYardMap.upper_bound(zoneId);
4851    if(graveLow==graveUp)
4852    {
4853        //sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
4854        return;
4855    }
4856
4857    bool found = false;
4858
4859    GraveYardMap::iterator itr;
4860
4861    for(itr = graveLow; itr != graveUp; ++itr)
4862    {
4863        GraveYardData & data = itr->second;
4864
4865        // skip not matching safezone id
4866        if(data.safeLocId != id)
4867            continue;
4868
4869        // skip enemy faction graveyard at same map (normal area, city, or battleground)
4870        // team == 0 case can be at call from .neargrave
4871        if(data.team != 0 && team != 0 && data.team != team)
4872            continue;
4873
4874        found = true;
4875        break;
4876    }
4877
4878    // no match, return
4879    if(!found)
4880        return;
4881
4882    // remove from links
4883    mGraveYardMap.erase(itr);
4884
4885    // remove link from DB
4886    if(inDB)
4887    {
4888        WorldDatabase.PExecute("DELETE FROM game_graveyard_zone WHERE id = '%u' AND ghost_zone = '%u' AND faction = '%u'",id,zoneId,team);
4889    }
4890
4891    return;
4892}
4893
4894
4895void ObjectMgr::LoadAreaTriggerTeleports()
4896{
4897    mAreaTriggers.clear();                                  // need for reload case
4898
4899    uint32 count = 0;
4900
4901    //                                                0   1               2              3               4           5            6                    7                     8           9                  10                 11                 12
4902    QueryResult *result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, heroic_key, heroic_key2, required_quest_done, required_failed_text, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
4903    if( !result )
4904    {
4905
4906        barGoLink bar( 1 );
4907
4908        bar.step();
4909
4910        sLog.outString();
4911        sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
4912        return;
4913    }
4914
4915    barGoLink bar( result->GetRowCount() );
4916
4917    do
4918    {
4919        Field *fields = result->Fetch();
4920
4921        bar.step();
4922
4923        ++count;
4924
4925        uint32 Trigger_ID = fields[0].GetUInt32();
4926
4927        AreaTrigger at;
4928
4929        at.requiredLevel      = fields[1].GetUInt8();
4930        at.requiredItem       = fields[2].GetUInt32();
4931        at.requiredItem2      = fields[3].GetUInt32();
4932        at.heroicKey          = fields[4].GetUInt32();
4933        at.heroicKey2         = fields[5].GetUInt32();
4934        at.requiredQuest      = fields[6].GetUInt32();
4935        at.requiredFailedText = fields[7].GetCppString();
4936        at.target_mapId       = fields[8].GetUInt32();
4937        at.target_X           = fields[9].GetFloat();
4938        at.target_Y           = fields[10].GetFloat();
4939        at.target_Z           = fields[11].GetFloat();
4940        at.target_Orientation = fields[12].GetFloat();
4941
4942        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
4943        if(!atEntry)
4944        {
4945            sLog.outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.",Trigger_ID);
4946            continue;
4947        }
4948
4949        if(at.requiredItem)
4950        {
4951            ItemPrototype const *pProto = GetItemPrototype(at.requiredItem);
4952            if(!pProto)
4953            {
4954                sLog.outError("Key item %u does not exist for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID);
4955                at.requiredItem = 0;
4956            }
4957        }
4958        if(at.requiredItem2)
4959        {
4960            ItemPrototype const *pProto = GetItemPrototype(at.requiredItem2);
4961            if(!pProto)
4962            {
4963                sLog.outError("Second item %u not exist for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID);
4964                at.requiredItem2 = 0;
4965            }
4966        }
4967
4968        if(at.heroicKey)
4969        {
4970            ItemPrototype const *pProto = GetItemPrototype(at.heroicKey);
4971            if(!pProto)
4972            {
4973                sLog.outError("Heroic key item %u not exist for trigger %u, remove key requirement.", at.heroicKey, Trigger_ID);
4974                at.heroicKey = 0;
4975            }
4976        }
4977
4978        if(at.heroicKey2)
4979        {
4980            ItemPrototype const *pProto = GetItemPrototype(at.heroicKey2);
4981            if(!pProto)
4982            {
4983                sLog.outError("Heroic second key item %u not exist for trigger %u, remove key requirement.", at.heroicKey2, Trigger_ID);
4984                at.heroicKey2 = 0;
4985            }
4986        }
4987
4988        if(at.requiredQuest)
4989        {
4990            if(!mQuestTemplates[at.requiredQuest])
4991            {
4992                sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",at.requiredQuest,Trigger_ID);
4993                at.requiredQuest = 0;
4994            }
4995        }
4996
4997        MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
4998        if(!mapEntry)
4999        {
5000            sLog.outErrorDb("Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Trigger_ID,at.target_mapId);
5001            continue;
5002        }
5003
5004        if(at.target_X==0 && at.target_Y==0 && at.target_Z==0)
5005        {
5006            sLog.outErrorDb("Area trigger (ID:%u) target coordinates not provided.",Trigger_ID);
5007            continue;
5008        }
5009
5010        mAreaTriggers[Trigger_ID] = at;
5011
5012    } while( result->NextRow() );
5013
5014    delete result;
5015
5016    sLog.outString();
5017    sLog.outString( ">> Loaded %u area trigger teleport definitions", count );
5018}
5019
5020AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
5021{
5022    const MapEntry *mapEntry = sMapStore.LookupEntry(Map);
5023    if(!mapEntry) return NULL;
5024    for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); itr++)
5025    {
5026        if(itr->second.target_mapId == mapEntry->parent_map)
5027        {
5028            AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
5029            if(atEntry && atEntry->mapid == Map)
5030                return &itr->second;
5031        }
5032    }
5033    return NULL;
5034}
5035
5036void ObjectMgr::SetHighestGuids()
5037{
5038    QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" );
5039    if( result )
5040    {
5041        m_hiCharGuid = (*result)[0].GetUInt32()+1;
5042
5043        delete result;
5044    }
5045
5046    result = WorldDatabase.Query( "SELECT MAX(guid) FROM creature" );
5047    if( result )
5048    {
5049        m_hiCreatureGuid = (*result)[0].GetUInt32()+1;
5050
5051        delete result;
5052    }
5053
5054    result = CharacterDatabase.Query( "SELECT MAX(id) FROM character_pet" );
5055    if( result )
5056    {
5057        m_hiPetGuid = (*result)[0].GetUInt32()+1;
5058
5059        delete result;
5060    }
5061
5062    result = CharacterDatabase.Query( "SELECT MAX(guid) FROM item_instance" );
5063    if( result )
5064    {
5065        m_hiItemGuid = (*result)[0].GetUInt32()+1;
5066
5067        delete result;
5068    }
5069
5070    // Cleanup other tables from not existed guids (>=m_hiItemGuid)
5071    CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_hiItemGuid);
5072    CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_hiItemGuid);
5073    CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", m_hiItemGuid);
5074    CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_hiItemGuid);
5075
5076    result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject" );
5077    if( result )
5078    {
5079        m_hiGoGuid = (*result)[0].GetUInt32()+1;
5080
5081        delete result;
5082    }
5083
5084    result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse" );
5085    if( result )
5086    {
5087        m_auctionid = (*result)[0].GetUInt32()+1;
5088
5089        delete result;
5090    }
5091    else
5092    {
5093        m_auctionid = 0;
5094    }
5095    result = CharacterDatabase.Query( "SELECT MAX(id) FROM mail" );
5096    if( result )
5097    {
5098        m_mailid = (*result)[0].GetUInt32()+1;
5099
5100        delete result;
5101    }
5102    else
5103    {
5104        m_mailid = 0;
5105    }
5106    result = CharacterDatabase.Query( "SELECT MAX(id) FROM item_text" );
5107    if( result )
5108    {
5109        m_ItemTextId = (*result)[0].GetUInt32();
5110
5111        delete result;
5112    }
5113    else
5114        m_ItemTextId = 0;
5115
5116    result = CharacterDatabase.Query( "SELECT MAX(guid) FROM corpse" );
5117    if( result )
5118    {
5119        m_hiCorpseGuid = (*result)[0].GetUInt32()+1;
5120
5121        delete result;
5122    }
5123}
5124
5125uint32 ObjectMgr::GenerateAuctionID()
5126{
5127    ++m_auctionid;
5128    if(m_auctionid>=0xFFFFFFFF)
5129    {
5130        sLog.outError("Auctions ids overflow!! Can't continue, shuting down server. ");
5131        sWorld.m_stopEvent = true;
5132    }
5133    return m_auctionid;
5134}
5135
5136uint32 ObjectMgr::GenerateMailID()
5137{
5138    ++m_mailid;
5139    if(m_mailid>=0xFFFFFFFF)
5140    {
5141        sLog.outError("Mail ids overflow!! Can't continue, shuting down server. ");
5142        sWorld.m_stopEvent = true;
5143    }
5144    return m_mailid;
5145}
5146
5147uint32 ObjectMgr::GenerateItemTextID()
5148{
5149    ++m_ItemTextId;
5150    if(m_ItemTextId>=0xFFFFFFFF)
5151    {
5152        sLog.outError("Item text ids overflow!! Can't continue, shuting down server. ");
5153        sWorld.m_stopEvent = true;
5154    }
5155    return m_ItemTextId;
5156}
5157
5158uint32 ObjectMgr::CreateItemText(std::string text)
5159{
5160    uint32 newItemTextId = GenerateItemTextID();
5161    //insert new itempage to container
5162    mItemTexts[ newItemTextId ] = text;
5163    //save new itempage
5164    CharacterDatabase.escape_string(text);
5165    //any Delete query needed, itemTextId is maximum of all ids
5166    std::ostringstream query;
5167    query << "INSERT INTO item_text (id,text) VALUES ( '" << newItemTextId << "', '" << text << "')";
5168    CharacterDatabase.Execute(query.str().c_str());         //needs to be run this way, because mail body may be more than 1024 characters
5169    return newItemTextId;
5170}
5171
5172uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
5173{
5174    switch(guidhigh)
5175    {
5176        case HIGHGUID_ITEM:
5177            ++m_hiItemGuid;
5178            if(m_hiItemGuid>=0xFFFFFFFF)
5179            {
5180                sLog.outError("Item guid overflow!! Can't continue, shuting down server. ");
5181                sWorld.m_stopEvent = true;
5182            }
5183            return m_hiItemGuid;
5184        case HIGHGUID_UNIT:
5185            ++m_hiCreatureGuid;
5186            if(m_hiCreatureGuid>=0x00FFFFFF)
5187            {
5188                sLog.outError("Creature guid overflow!! Can't continue, shuting down server. ");
5189                sWorld.m_stopEvent = true;
5190            }
5191            return m_hiCreatureGuid;
5192        case HIGHGUID_PET:
5193            ++m_hiPetGuid;
5194            if(m_hiPetGuid>=0x00FFFFFF)
5195            {
5196                sLog.outError("Pet guid overflow!! Can't continue, shuting down server. ");
5197                sWorld.m_stopEvent = true;
5198            }
5199            return m_hiPetGuid;
5200        case HIGHGUID_PLAYER:
5201            ++m_hiCharGuid;
5202            if(m_hiCharGuid>=0xFFFFFFFF)
5203            {
5204                sLog.outError("Players guid overflow!! Can't continue, shuting down server. ");
5205                sWorld.m_stopEvent = true;
5206            }
5207            return m_hiCharGuid;
5208        case HIGHGUID_GAMEOBJECT:
5209            ++m_hiGoGuid;
5210            if(m_hiGoGuid>=0x00FFFFFF)
5211            {
5212                sLog.outError("Gameobject guid overflow!! Can't continue, shuting down server. ");
5213                sWorld.m_stopEvent = true;
5214            }
5215            return m_hiGoGuid;
5216        case HIGHGUID_CORPSE:
5217            ++m_hiCorpseGuid;
5218            if(m_hiCorpseGuid>=0xFFFFFFFF)
5219            {
5220                sLog.outError("Corpse guid overflow!! Can't continue, shuting down server. ");
5221                sWorld.m_stopEvent = true;
5222            }
5223            return m_hiCorpseGuid;
5224        case HIGHGUID_DYNAMICOBJECT:
5225            ++m_hiDoGuid;
5226            if(m_hiDoGuid>=0xFFFFFFFF)
5227            {
5228                sLog.outError("DynamicObject guid overflow!! Can't continue, shuting down server. ");
5229                sWorld.m_stopEvent = true;
5230            }
5231            return m_hiDoGuid;
5232        default:
5233            ASSERT(0);
5234    }
5235
5236    ASSERT(0);
5237    return 0;
5238}
5239
5240void ObjectMgr::LoadGameObjectLocales()
5241{
5242    mGameObjectLocaleMap.clear();
5243   
5244    QueryResult *result = WorldDatabase.Query("SELECT entry,"
5245        "name_loc1,name_loc2,name_loc3,name_loc4,name_loc5,name_loc6,name_loc7,name_loc8,"
5246        "castbarcaption_loc1,castbarcaption_loc2,castbarcaption_loc3,castbarcaption_loc4,"
5247        "castbarcaption_loc5,castbarcaption_loc6,castbarcaption_loc7,castbarcaption_loc8 FROM locales_gameobject");
5248
5249    if(!result)
5250    {
5251        barGoLink bar(1);
5252
5253        bar.step();
5254
5255        sLog.outString("");
5256        sLog.outString(">> Loaded 0 gameobject locale strings. DB table `locales_gameobject` is empty.");
5257        return;
5258    }
5259
5260    barGoLink bar(result->GetRowCount());
5261
5262    do
5263    {
5264        Field *fields = result->Fetch();
5265        bar.step();
5266
5267        uint32 entry = fields[0].GetUInt32();
5268
5269        GameObjectLocale& data = mGameObjectLocaleMap[entry];
5270
5271        for(int i = 1; i < MAX_LOCALE; ++i)
5272        {
5273            std::string str = fields[i].GetCppString();
5274            if(!str.empty())
5275            {
5276                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5277                if(idx >= 0)
5278                {
5279                    if(data.Name.size() <= idx)
5280                        data.Name.resize(idx+1);
5281
5282                    data.Name[idx] = str;
5283                }
5284            }
5285        }
5286
5287        for(int i = MAX_LOCALE; i < MAX_LOCALE*2-1; ++i)
5288        {
5289            std::string str = fields[i].GetCppString();
5290            if(!str.empty())
5291            {
5292                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
5293                if(idx >= 0)
5294                {
5295                    if(data.CastBarCaption.size() <= idx)
5296                        data.CastBarCaption.resize(idx+1);
5297
5298                    data.CastBarCaption[idx] = str;
5299                }
5300            }
5301        }
5302
5303    } while (result->NextRow());
5304
5305    delete result;
5306
5307    sLog.outString();
5308    sLog.outString( ">> Loaded %u gameobject locale strings", mGameObjectLocaleMap.size() );
5309}
5310
5311void ObjectMgr::LoadGameobjectInfo()
5312{
5313    sGOStorage.Load();
5314
5315    // some checks
5316    for(uint32 id = 1; id < sGOStorage.MaxEntry; id++)
5317    {
5318        GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
5319        if(!goInfo)
5320            continue;
5321
5322        switch(goInfo->type)
5323        {
5324            case GAMEOBJECT_TYPE_DOOR:                      //0
5325            {
5326                if(goInfo->door.lockId)
5327                {
5328                    if(!sLockStore.LookupEntry(goInfo->door.lockId))
5329                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
5330                            id,goInfo->type,goInfo->door.lockId,goInfo->door.lockId);
5331                }
5332                break;
5333            }
5334            case GAMEOBJECT_TYPE_BUTTON:                    //1
5335            {
5336                if(goInfo->button.lockId)
5337                {
5338                    if(!sLockStore.LookupEntry(goInfo->button.lockId))
5339                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but lock (Id: %u) not found.",
5340                            id,goInfo->type,goInfo->button.lockId,goInfo->button.lockId);
5341                }
5342                break;
5343            }
5344            case GAMEOBJECT_TYPE_CHEST:                     //3
5345            {
5346                if(goInfo->chest.lockId)
5347                {
5348                    if(!sLockStore.LookupEntry(goInfo->chest.lockId))
5349                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but lock (Id: %u) not found.",
5350                            id,goInfo->type,goInfo->chest.lockId,goInfo->chest.lockId);
5351                }
5352                if(goInfo->chest.linkedTrapId)              // linked trap
5353                {
5354                    if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->chest.linkedTrapId))
5355                    {
5356                        if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
5357                            sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
5358                                id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
5359                    }
5360                    /* disable check for while
5361                    else
5362                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
5363                            id,goInfo->type,goInfo->chest.linkedTrapId,goInfo->chest.linkedTrapId);
5364                    */
5365                }
5366                break;
5367            }
5368            case GAMEOBJECT_TYPE_TRAP:                      //6
5369            {
5370                /* disable check for while
5371                if(goInfo->trap.spellId)                    // spell
5372                {
5373                    if(!sSpellStore.LookupEntry(goInfo->trap.spellId))
5374                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
5375                            id,goInfo->type,goInfo->trap.spellId,goInfo->trap.spellId);
5376                }
5377                */
5378                break;
5379            }
5380            case GAMEOBJECT_TYPE_CHAIR:                     //7
5381                if(goInfo->chair.height > 2)
5382                {
5383                    sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but correct chair height in range 0..2.",
5384                        id,goInfo->type,goInfo->chair.height);
5385
5386                    // prevent client and server unexpected work
5387                    const_cast<GameObjectInfo*>(goInfo)->chair.height = 0;
5388                }
5389                break;
5390            case GAMEOBJECT_TYPE_SPELL_FOCUS:               //8
5391            {
5392                if(goInfo->spellFocus.focusId)
5393                {
5394                    if(!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
5395                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.",
5396                            id,goInfo->type,goInfo->spellFocus.focusId,goInfo->spellFocus.focusId);
5397                }
5398
5399                if(goInfo->spellFocus.linkedTrapId)         // linked trap
5400                {
5401                    if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->spellFocus.linkedTrapId))
5402                    {
5403                        if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
5404                            sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
5405                                id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
5406                    }
5407                    /* disable check for while
5408                    else
5409                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
5410                            id,goInfo->type,goInfo->spellFocus.linkedTrapId,goInfo->spellFocus.linkedTrapId);
5411                    */
5412                }
5413                break;
5414            }
5415            case GAMEOBJECT_TYPE_GOOBER:                    //10
5416            {
5417                if(goInfo->goober.pageId)                   // pageId
5418                {
5419                    if(!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
5420                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.",
5421                            id,goInfo->type,goInfo->goober.pageId,goInfo->goober.pageId);
5422                }
5423                /* disable check for while
5424                if(goInfo->goober.spellId)                  // spell
5425                {
5426                    if(!sSpellStore.LookupEntry(goInfo->goober.spellId))
5427                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data2=%u but Spell (Entry %u) not exist.",
5428                            id,goInfo->type,goInfo->goober.spellId,goInfo->goober.spellId);
5429                }
5430                */
5431                if(goInfo->goober.linkedTrapId)             // linked trap
5432                {
5433                    if(GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(goInfo->goober.linkedTrapId))
5434                    {
5435                        if(trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
5436                            sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.",
5437                                id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId,GAMEOBJECT_TYPE_TRAP);
5438                    }
5439                    /* disable check for while
5440                    else
5441                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data12=%u but trap GO (Entry %u) not exist in `gameobject_template`.",
5442                            id,goInfo->type,goInfo->goober.linkedTrapId,goInfo->goober.linkedTrapId);
5443                    */
5444                }
5445                break;
5446            }
5447            case GAMEOBJECT_TYPE_MO_TRANSPORT:              //15
5448            {
5449                if(goInfo->moTransport.taxiPathId)
5450                {
5451                    if(goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
5452                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.",
5453                            id,goInfo->type,goInfo->moTransport.taxiPathId,goInfo->moTransport.taxiPathId);
5454                }
5455                break;
5456            }
5457            case GAMEOBJECT_TYPE_SUMMONING_RITUAL:          //18
5458            {
5459                /* disabled
5460                if(goInfo->summoningRitual.spellId)
5461                {
5462                    if(!sSpellStore.LookupEntry(goInfo->summoningRitual.spellId))
5463                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data1=%u but Spell (Entry %u) not exist.",
5464                            id,goInfo->type,goInfo->summoningRitual.spellId,goInfo->summoningRitual.spellId);
5465                }
5466                */
5467                break;
5468            }
5469            case GAMEOBJECT_TYPE_SPELLCASTER:               //22
5470            {
5471                if(goInfo->spellcaster.spellId)             // spell
5472                {
5473                    if(!sSpellStore.LookupEntry(goInfo->spellcaster.spellId))
5474                        sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data3=%u but Spell (Entry %u) not exist.",
5475                            id,goInfo->type,goInfo->spellcaster.spellId,goInfo->spellcaster.spellId);
5476                }
5477                break;
5478            }
5479        }
5480    }
5481
5482    sLog.outString( ">> Loaded %u game object templates", sGOStorage.RecordCount );
5483    sLog.outString();
5484}
5485
5486void ObjectMgr::LoadExplorationBaseXP()
5487{
5488    uint32 count = 0;
5489    QueryResult *result = WorldDatabase.Query("SELECT level,basexp FROM exploration_basexp");
5490
5491    if( !result )
5492    {
5493        barGoLink bar( 1 );
5494
5495        bar.step();
5496
5497        sLog.outString();
5498        sLog.outString( ">> Loaded %u BaseXP definitions", count );
5499        return;
5500    }
5501
5502    barGoLink bar( result->GetRowCount() );
5503
5504    do
5505    {
5506        bar.step();
5507
5508        Field *fields = result->Fetch();
5509        uint32 level  = fields[0].GetUInt32();
5510        uint32 basexp = fields[1].GetUInt32();
5511        mBaseXPTable[level] = basexp;
5512        ++count;
5513    }
5514    while (result->NextRow());
5515
5516    delete result;
5517
5518    sLog.outString();
5519    sLog.outString( ">> Loaded %u BaseXP definitions", count );
5520}
5521
5522uint32 ObjectMgr::GetBaseXP(uint32 level)
5523{
5524    return mBaseXPTable[level] ? mBaseXPTable[level] : 0;
5525}
5526
5527void ObjectMgr::LoadPetNames()
5528{
5529    uint32 count = 0;
5530    QueryResult *result = WorldDatabase.Query("SELECT word,entry,half FROM pet_name_generation");
5531
5532    if( !result )
5533    {
5534        barGoLink bar( 1 );
5535
5536        bar.step();
5537
5538        sLog.outString();
5539        sLog.outString( ">> Loaded %u pet name parts", count );
5540        return;
5541    }
5542
5543    barGoLink bar( result->GetRowCount() );
5544
5545    do
5546    {
5547        bar.step();
5548
5549        Field *fields = result->Fetch();
5550        std::string word = fields[0].GetString();
5551        uint32 entry     = fields[1].GetUInt32();
5552        bool   half      = fields[2].GetBool();
5553        if(half)
5554            PetHalfName1[entry].push_back(word);
5555        else
5556            PetHalfName0[entry].push_back(word);
5557        ++count;
5558    }
5559    while (result->NextRow());
5560    delete result;
5561
5562    sLog.outString();
5563    sLog.outString( ">> Loaded %u pet name parts", count );
5564}
5565
5566void ObjectMgr::LoadPetNumber()
5567{
5568    QueryResult* result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
5569    if(result)
5570    {
5571        Field *fields = result->Fetch();
5572        m_hiPetNumber = fields[0].GetUInt32()+1;
5573        delete result;
5574    }
5575
5576    barGoLink bar( 1 );
5577    bar.step();
5578
5579    sLog.outString();
5580    sLog.outString( ">> Loaded the max pet number: %d", m_hiPetNumber-1);
5581}
5582
5583std::string ObjectMgr::GeneratePetName(uint32 entry)
5584{
5585    std::vector<std::string> & list0 = PetHalfName0[entry];
5586    std::vector<std::string> & list1 = PetHalfName1[entry];
5587
5588    if(list0.empty() || list1.empty())
5589    {
5590        CreatureInfo const *cinfo = GetCreatureTemplate(entry);
5591        char* petname = GetPetName(cinfo->family, sWorld.GetDefaultDbcLocale());
5592        if(!petname)
5593            petname = cinfo->Name;
5594        return std::string(petname);
5595    }
5596
5597    return *(list0.begin()+urand(0, list0.size()-1)) + *(list1.begin()+urand(0, list1.size()-1));
5598}
5599
5600uint32 ObjectMgr::GeneratePetNumber()
5601{
5602    return ++m_hiPetNumber;
5603}
5604
5605void ObjectMgr::LoadCorpses()
5606{
5607    uint32 count = 0;
5608    //                                                     0           1           2           3            4    5     6     7            8         10
5609    QueryResult *result = CharacterDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map, data, time, corpse_type, instance, guid FROM corpse WHERE corpse_type <> 0");
5610
5611    if( !result )
5612    {
5613        barGoLink bar( 1 );
5614
5615        bar.step();
5616
5617        sLog.outString();
5618        sLog.outString( ">> Loaded %u corpses", count );
5619        return;
5620    }
5621
5622    barGoLink bar( result->GetRowCount() );
5623
5624    do
5625    {
5626        bar.step();
5627
5628        Field *fields = result->Fetch();
5629
5630        uint32 guid = fields[result->GetFieldCount()-1].GetUInt32();
5631
5632        Corpse *corpse = new Corpse;
5633        if(!corpse->LoadFromDB(guid,fields))
5634        {
5635            delete corpse;
5636            continue;
5637        }
5638
5639        ObjectAccessor::Instance().AddCorpse(corpse);
5640
5641        ++count;
5642    }
5643    while (result->NextRow());
5644    delete result;
5645
5646    sLog.outString();
5647    sLog.outString( ">> Loaded %u corpses", count );
5648}
5649
5650void ObjectMgr::LoadReputationOnKill()
5651{
5652    uint32 count = 0;
5653
5654    //                                                0            1                     2
5655    QueryResult *result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2,"
5656    //   3             4             5                   6             7             8                   9
5657        "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent "
5658        "FROM creature_onkill_reputation");
5659
5660    if(!result)
5661    {
5662        barGoLink bar(1);
5663
5664        bar.step();
5665
5666        sLog.outString();
5667        sLog.outErrorDb(">> Loaded 0 creature award reputation definitions. DB table `creature_onkill_reputation` is empty.");
5668        return;
5669    }
5670
5671    barGoLink bar(result->GetRowCount());
5672
5673    do
5674    {
5675        Field *fields = result->Fetch();
5676        bar.step();
5677
5678        uint32 creature_id = fields[0].GetUInt32();
5679
5680        ReputationOnKillEntry repOnKill;
5681        repOnKill.repfaction1          = fields[1].GetUInt32();
5682        repOnKill.repfaction2          = fields[2].GetUInt32();
5683        repOnKill.is_teamaward1        = fields[3].GetBool();
5684        repOnKill.reputation_max_cap1  = fields[4].GetUInt32();
5685        repOnKill.repvalue1            = fields[5].GetInt32();
5686        repOnKill.is_teamaward2        = fields[6].GetBool();
5687        repOnKill.reputation_max_cap2  = fields[7].GetUInt32();
5688        repOnKill.repvalue2            = fields[8].GetInt32();
5689        repOnKill.team_dependent       = fields[9].GetUInt8();
5690
5691        if(!GetCreatureTemplate(creature_id))
5692        {
5693            sLog.outErrorDb("Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped",creature_id);
5694            continue;
5695        }
5696
5697        if(repOnKill.repfaction1)
5698        {
5699            FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
5700            if(!factionEntry1)
5701            {
5702                sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction1);
5703                continue;
5704            }
5705        }
5706
5707        if(repOnKill.repfaction2)
5708        {
5709            FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
5710            if(!factionEntry2)
5711            {
5712                sLog.outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reputation`",repOnKill.repfaction2);
5713                continue;
5714            }
5715        }
5716
5717        mRepOnKill[creature_id] = repOnKill;
5718
5719        ++count;
5720    } while (result->NextRow());
5721
5722    delete result;
5723
5724    sLog.outString();
5725    sLog.outString(">> Loaded %u creature award reputation definitions", count);
5726}
5727
5728void ObjectMgr::LoadWeatherZoneChances()
5729{
5730    uint32 count = 0;
5731
5732    //                                                0     1                   2                   3                    4                   5                   6                    7                 8                 9                  10                  11                  12
5733    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");
5734
5735    if(!result)
5736    {
5737        barGoLink bar(1);
5738
5739        bar.step();
5740
5741        sLog.outString();
5742        sLog.outErrorDb(">> Loaded 0 weather definitions. DB table `game_weather` is empty.");
5743        return;
5744    }
5745
5746    barGoLink bar(result->GetRowCount());
5747
5748    do
5749    {
5750        Field *fields = result->Fetch();
5751        bar.step();
5752
5753        uint32 zone_id = fields[0].GetUInt32();
5754
5755        WeatherZoneChances& wzc = mWeatherZoneMap[zone_id];
5756
5757        for(int season = 0; season < WEATHER_SEASONS; ++season)
5758        {
5759            wzc.data[season].rainChance  = fields[season * (MAX_WEATHER_TYPE-1) + 1].GetUInt32();
5760            wzc.data[season].snowChance  = fields[season * (MAX_WEATHER_TYPE-1) + 2].GetUInt32();
5761            wzc.data[season].stormChance = fields[season * (MAX_WEATHER_TYPE-1) + 3].GetUInt32();
5762
5763            if(wzc.data[season].rainChance > 100)
5764            {
5765                wzc.data[season].rainChance = 25;
5766                sLog.outErrorDb("Weather for zone %u season %u has wrong rain chance > 100%",zone_id,season);
5767            }
5768
5769            if(wzc.data[season].snowChance > 100)
5770            {
5771                wzc.data[season].snowChance = 25;
5772                sLog.outErrorDb("Weather for zone %u season %u has wrong snow chance > 100%",zone_id,season);
5773            }
5774
5775            if(wzc.data[season].stormChance > 100)
5776            {
5777                wzc.data[season].stormChance = 25;
5778                sLog.outErrorDb("Weather for zone %u season %u has wrong storm chance > 100%",zone_id,season);
5779            }
5780        }
5781
5782        ++count;
5783    } while (result->NextRow());
5784
5785    delete result;
5786
5787    sLog.outString();
5788    sLog.outString(">> Loaded %u weather definitions", count);
5789}
5790
5791void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t)
5792{
5793    mCreatureRespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
5794    WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
5795    if(t)
5796        WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
5797}
5798
5799void ObjectMgr::DeleteCreatureData(uint32 guid)
5800{
5801    // remove mapid*cellid -> guid_set map
5802    CreatureData const* data = GetCreatureData(guid);
5803    if(data)
5804        RemoveCreatureFromGrid(guid, data);
5805
5806    mCreatureDataMap.erase(guid);
5807}
5808
5809void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t)
5810{
5811    mGORespawnTimes[MAKE_PAIR64(loguid,instance)] = t;
5812    WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE guid = '%u' AND instance = '%u'", loguid, instance);
5813    if(t)
5814        WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" I64FMTD "', '%u' )", loguid, uint64(t), instance);
5815}
5816
5817void ObjectMgr::DeleteRespawnTimeForInstance(uint32 instance)
5818{
5819    RespawnTimes::iterator next;
5820
5821    for(RespawnTimes::iterator itr = mGORespawnTimes.begin(); itr != mGORespawnTimes.end(); itr = next)
5822    {
5823        next = itr;
5824        ++next;
5825
5826        if(GUID_HIPART(itr->first)==instance)
5827            mGORespawnTimes.erase(itr);
5828    }
5829
5830    for(RespawnTimes::iterator itr = mCreatureRespawnTimes.begin(); itr != mCreatureRespawnTimes.end(); itr = next)
5831    {
5832        next = itr;
5833        ++next;
5834
5835        if(GUID_HIPART(itr->first)==instance)
5836            mCreatureRespawnTimes.erase(itr);
5837    }
5838
5839    WorldDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", instance);
5840    WorldDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", instance);
5841}
5842
5843void ObjectMgr::DeleteGOData(uint32 guid)
5844{
5845    // remove mapid*cellid -> guid_set map
5846    GameObjectData const* data = GetGOData(guid);
5847    if(data)
5848        RemoveGameobjectFromGrid(guid, data);
5849
5850    mGameObjectDataMap.erase(guid);
5851}
5852
5853void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
5854{
5855    // corpses are always added to spawn mode 0 and they are spawned by their instance id
5856    CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
5857    cell_guids.corpses[player_guid] = instance;
5858}
5859
5860void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid)
5861{
5862    // corpses are always added to spawn mode 0 and they are spawned by their instance id
5863    CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid,0)][cellid];
5864    cell_guids.corpses.erase(player_guid);
5865}
5866
5867void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map,char const* table)
5868{
5869    map.clear();                                            // need for reload case
5870
5871    uint32 count = 0;
5872
5873    QueryResult *result = WorldDatabase.PQuery("SELECT id,quest FROM %s",table);
5874
5875    if(!result)
5876    {
5877        barGoLink bar(1);
5878
5879        bar.step();
5880
5881        sLog.outString();
5882        sLog.outErrorDb(">> Loaded 0 quest relations from %s. DB table `%s` is empty.",table,table);
5883        return;
5884    }
5885
5886    barGoLink bar(result->GetRowCount());
5887
5888    do
5889    {
5890        Field *fields = result->Fetch();
5891        bar.step();
5892
5893        uint32 id    = fields[0].GetUInt32();
5894        uint32 quest = fields[1].GetUInt32();
5895
5896        if(mQuestTemplates.find(quest) == mQuestTemplates.end())
5897        {
5898            sLog.outErrorDb("Table `%s: Quest %u listed for entry %u does not exist.",table,quest,id);
5899            continue;
5900        }
5901
5902        map.insert(QuestRelations::value_type(id,quest));
5903
5904        ++count;
5905    } while (result->NextRow());
5906
5907    delete result;
5908
5909    sLog.outString();
5910    sLog.outString(">> Loaded %u quest relations from %s", count,table);
5911}
5912
5913void ObjectMgr::LoadGameobjectQuestRelations()
5914{
5915    LoadQuestRelationsHelper(mGOQuestRelations,"gameobject_questrelation");
5916
5917    for(QuestRelations::iterator itr = mGOQuestRelations.begin(); itr != mGOQuestRelations.end(); ++itr)
5918    {
5919        GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
5920        if(!goInfo)
5921            sLog.outErrorDb("Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
5922        else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
5923            sLog.outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
5924    }
5925}
5926
5927void ObjectMgr::LoadGameobjectInvolvedRelations()
5928{
5929    LoadQuestRelationsHelper(mGOQuestInvolvedRelations,"gameobject_involvedrelation");
5930
5931    for(QuestRelations::iterator itr = mGOQuestInvolvedRelations.begin(); itr != mGOQuestInvolvedRelations.end(); ++itr)
5932    {
5933        GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
5934        if(!goInfo)
5935            sLog.outErrorDb("Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u",itr->first,itr->second);
5936        else if(goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
5937            sLog.outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER",itr->first,itr->second);
5938    }
5939}
5940
5941void ObjectMgr::LoadCreatureQuestRelations()
5942{
5943    LoadQuestRelationsHelper(mCreatureQuestRelations,"creature_questrelation");
5944
5945    for(QuestRelations::iterator itr = mCreatureQuestRelations.begin(); itr != mCreatureQuestRelations.end(); ++itr)
5946    {
5947        CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
5948        if(!cInfo)
5949            sLog.outErrorDb("Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
5950        else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
5951            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);
5952    }
5953}
5954
5955void ObjectMgr::LoadCreatureInvolvedRelations()
5956{
5957    LoadQuestRelationsHelper(mCreatureQuestInvolvedRelations,"creature_involvedrelation");
5958
5959    for(QuestRelations::iterator itr = mCreatureQuestInvolvedRelations.begin(); itr != mCreatureQuestInvolvedRelations.end(); ++itr)
5960    {
5961        CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
5962        if(!cInfo)
5963            sLog.outErrorDb("Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u",itr->first,itr->second);
5964        else if(!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
5965            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);
5966    }
5967}
5968
5969void ObjectMgr::LoadReservedPlayersNames()
5970{
5971    m_ReservedNames.clear();                                // need for reload case
5972
5973    QueryResult *result = WorldDatabase.PQuery("SELECT name FROM reserved_name");
5974
5975    uint32 count = 0;
5976
5977    if( !result )
5978    {
5979        barGoLink bar( 1 );
5980        bar.step();
5981
5982        sLog.outString();
5983        sLog.outString( ">> Loaded %u reserved player names", count );
5984        return;
5985    }
5986
5987    barGoLink bar( result->GetRowCount() );
5988
5989    Field* fields;
5990    do
5991    {
5992        bar.step();
5993        fields = result->Fetch();
5994        std::string name= fields[0].GetCppString();
5995        if(normalizePlayerName(name))
5996        {
5997            m_ReservedNames.insert(name);
5998            ++count;
5999        }
6000    } while ( result->NextRow() );
6001
6002    delete result;
6003
6004    sLog.outString();
6005    sLog.outString( ">> Loaded %u reserved player names", count );
6006}
6007
6008enum LanguageType
6009{
6010    LT_BASIC_LATIN    = 0x0000,
6011    LT_EXTENDEN_LATIN = 0x0001,
6012    LT_CYRILLIC       = 0x0002,
6013    LT_EAST_ASIA      = 0x0004,
6014    LT_ANY            = 0xFFFF
6015};
6016
6017static LanguageType GetRealmLanguageType(bool create)
6018{
6019    switch(sWorld.getConfig(CONFIG_REALM_ZONE))
6020    {
6021        case REALM_ZONE_UNKNOWN:                            // any language
6022        case REALM_ZONE_DEVELOPMENT:
6023        case REALM_ZONE_TEST_SERVER:
6024        case REALM_ZONE_QA_SERVER:
6025            return LT_ANY;
6026        case REALM_ZONE_UNITED_STATES:                      // extended-Latin
6027        case REALM_ZONE_OCEANIC:
6028        case REALM_ZONE_LATIN_AMERICA:
6029        case REALM_ZONE_ENGLISH:
6030        case REALM_ZONE_GERMAN:
6031        case REALM_ZONE_FRENCH:
6032        case REALM_ZONE_SPANISH:
6033            return LT_EXTENDEN_LATIN;
6034        case REALM_ZONE_KOREA:                              // East-Asian
6035        case REALM_ZONE_TAIWAN:
6036        case REALM_ZONE_CHINA:
6037            return LT_EAST_ASIA;
6038        case REALM_ZONE_RUSSIAN:                            // Cyrillic
6039            return LT_CYRILLIC;
6040        default:
6041            return create ? LT_BASIC_LATIN : LT_ANY;        // basic-Latin at create, any at login
6042    }
6043}
6044
6045bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
6046{
6047    if(strictMask==0)                                       // any language, ignore realm
6048    {
6049        if(isExtendedLatinString(wstr,numericOrSpace))
6050            return true;
6051        if(isCyrillicString(wstr,numericOrSpace))
6052            return true;
6053        if(isEastAsianString(wstr,numericOrSpace))
6054            return true;
6055        return false;
6056    }
6057
6058    if(strictMask & 0x2)                                    // realm zone specific
6059    {
6060        LanguageType lt = GetRealmLanguageType(create);
6061        if(lt & LT_EXTENDEN_LATIN)
6062            if(isExtendedLatinString(wstr,numericOrSpace))
6063                return true;
6064        if(lt & LT_CYRILLIC)
6065            if(isCyrillicString(wstr,numericOrSpace))
6066                return true;
6067        if(lt & LT_EAST_ASIA)
6068            if(isEastAsianString(wstr,numericOrSpace))
6069                return true;
6070    }
6071
6072    if(strictMask & 0x1)                                    // basic latin
6073    {
6074        if(isBasicLatinString(wstr,numericOrSpace))
6075            return true;
6076    }
6077
6078    return false;
6079}
6080
6081bool ObjectMgr::IsValidName( std::string name, bool create )
6082{
6083    std::wstring wname;
6084    if(!Utf8toWStr(name,wname))
6085        return false;
6086
6087    if(wname.size() < 1 || wname.size() > MAX_PLAYER_NAME)
6088        return false;
6089
6090    uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES);
6091
6092    return isValidString(wname,strictMask,false,create);
6093}
6094
6095bool ObjectMgr::IsValidCharterName( std::string name )
6096{
6097    std::wstring wname;
6098    if(!Utf8toWStr(name,wname))
6099        return false;
6100
6101    if(wname.size() < 1)
6102        return false;
6103
6104    uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES);
6105
6106    return isValidString(wname,strictMask,true);
6107}
6108
6109bool ObjectMgr::IsValidPetName( std::string name )
6110{
6111    std::wstring wname;
6112    if(!Utf8toWStr(name,wname))
6113        return false;
6114
6115    if(wname.size() < 1)
6116        return false;
6117
6118    uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES);
6119
6120    return isValidString(wname,strictMask,false);
6121}
6122
6123int ObjectMgr::GetIndexForLocale( LocaleConstant loc )
6124{
6125    if(loc==LOCALE_enUS)
6126        return -1;
6127
6128    for(size_t i=0;i < m_LocalForIndex.size(); ++i)
6129        if(m_LocalForIndex[i]==loc)
6130            return i;
6131
6132    return -1;
6133}
6134
6135LocaleConstant ObjectMgr::GetLocaleForIndex(int i)
6136{
6137    if (i<0 || i>=m_LocalForIndex.size())
6138        return LOCALE_enUS;
6139
6140    return m_LocalForIndex[i];
6141}
6142
6143int ObjectMgr::GetOrNewIndexForLocale( LocaleConstant loc )
6144{
6145    if(loc==LOCALE_enUS)
6146        return -1;
6147
6148    for(size_t i=0;i < m_LocalForIndex.size(); ++i)
6149        if(m_LocalForIndex[i]==loc)
6150            return i;
6151
6152    m_LocalForIndex.push_back(loc);
6153    return m_LocalForIndex.size()-1;
6154}
6155
6156void ObjectMgr::LoadBattleMastersEntry()
6157{
6158    mBattleMastersMap.clear();                              // need for reload case
6159
6160    QueryResult *result = WorldDatabase.Query( "SELECT entry,bg_template FROM battlemaster_entry" );
6161
6162    uint32 count = 0;
6163
6164    if( !result )
6165    {
6166        barGoLink bar( 1 );
6167        bar.step();
6168
6169        sLog.outString();
6170        sLog.outString( ">> Loaded 0 battlemaster entries - table is empty!" );
6171        return;
6172    }
6173
6174    barGoLink bar( result->GetRowCount() );
6175
6176    do
6177    {
6178        ++count;
6179        bar.step();
6180
6181        Field *fields = result->Fetch();
6182
6183        uint32 entry = fields[0].GetUInt32();
6184        uint32 bgTypeId  = fields[1].GetUInt32();
6185
6186        mBattleMastersMap[entry] = bgTypeId;
6187
6188    } while( result->NextRow() );
6189
6190    delete result;
6191
6192    sLog.outString();
6193    sLog.outString( ">> Loaded %u battlemaster entries", count );
6194}
6195
6196void ObjectMgr::LoadGameObjectForQuests()
6197{
6198    mGameObjectForQuestSet.clear();                         // need for reload case
6199
6200    uint32 count = 0;
6201
6202    // collect GO entries for GO that must activated
6203    for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
6204    {
6205        GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
6206        if(!goInfo)
6207            continue;
6208
6209        switch(goInfo->type)
6210        {
6211            // scan GO chest with loot including quest items
6212            case GAMEOBJECT_TYPE_CHEST:
6213            {
6214                uint32 loot_id = GameObject::GetLootId(goInfo);
6215
6216                // find quest loot for GO
6217                if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
6218                {
6219                    mGameObjectForQuestSet.insert(go_entry);
6220                    ++count;
6221                }
6222                break;
6223            }
6224            case GAMEOBJECT_TYPE_GOOBER:
6225            {
6226                if(goInfo->goober.questId)                  //quests objects
6227                {
6228                    mGameObjectForQuestSet.insert(go_entry);
6229                    count++;
6230                }
6231                break;
6232            }
6233            default:
6234                break;
6235        }
6236    }
6237
6238    sLog.outString();
6239    sLog.outString( ">> Loaded %u GameObject for quests", count );
6240}
6241
6242bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value)
6243{
6244    // cleanup affected map part for reloading case
6245    for(TrinityStringLocaleMap::iterator itr = mTrinityStringLocaleMap.begin(); itr != mTrinityStringLocaleMap.end();)
6246    {
6247        if(itr->first >= min_value && itr->first <= max_value)
6248        {
6249            TrinityStringLocaleMap::iterator itr2 = itr;
6250            ++itr;
6251            mTrinityStringLocaleMap.erase(itr2);
6252        }
6253        else
6254            ++itr;
6255    }
6256
6257    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);
6258
6259    if(!result)
6260    {
6261        barGoLink bar(1);
6262
6263        bar.step();
6264
6265        sLog.outString("");
6266        if(min_value > 0)                                   // error only in case internal strings
6267            sLog.outErrorDb(">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.",table);
6268        else
6269            sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.",table);
6270        return false;
6271    }
6272
6273    uint32 count = 0;
6274
6275    barGoLink bar(result->GetRowCount());
6276
6277    do
6278    {
6279        Field *fields = result->Fetch();
6280        bar.step();
6281
6282        int32 entry = fields[0].GetInt32();
6283
6284        if(entry==0)
6285        {
6286            sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
6287            continue;
6288        }
6289        else if(entry < min_value || entry > max_value)
6290        {
6291            int32 start = min_value > 0 ? min_value : max_value;
6292            int32 end   = min_value > 0 ? max_value : min_value;
6293            sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
6294            continue;
6295        }
6296
6297        TrinityStringLocale& data = mTrinityStringLocaleMap[entry];
6298
6299        if(data.Content.size() > 0)
6300        {
6301            sLog.outErrorDb("Table `%s` contain data for already loaded entry  %i (from another table?), ignored.",table,entry);
6302            continue;
6303        }
6304
6305        data.Content.resize(1);
6306        ++count;
6307
6308        // 0 -> default, idx in to idx+1
6309        data.Content[0] = fields[1].GetCppString();
6310
6311        for(int i = 1; i < MAX_LOCALE; ++i)
6312        {
6313            std::string str = fields[i+1].GetCppString();
6314            if(!str.empty())
6315            {
6316                int idx = GetOrNewIndexForLocale(LocaleConstant(i));
6317                if(idx >= 0)
6318                {
6319                    // 0 -> default, idx in to idx+1
6320                    if(data.Content.size() <= idx+1)
6321                        data.Content.resize(idx+2);
6322
6323                    data.Content[idx+1] = str;
6324                }
6325            }
6326        }
6327    } while (result->NextRow());
6328
6329    delete result;
6330
6331    sLog.outString();
6332    if(min_value > 0)                                       // internal Trinity strings
6333        sLog.outString( ">> Loaded %u Trinity strings from table %s", count,table);
6334    else
6335        sLog.outString( ">> Loaded %u string templates from %s", count,table);
6336
6337    return true;
6338}
6339
6340const char *ObjectMgr::GetTrinityString(int32 entry, int locale_idx) const
6341{
6342    // locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
6343    // Content[0] always exist if exist TrinityStringLocale
6344    if(TrinityStringLocale const *msl = GetTrinityStringLocale(entry))
6345    {
6346        if(msl->Content.size() > locale_idx+1 && !msl->Content[locale_idx+1].empty())
6347            return msl->Content[locale_idx+1].c_str();
6348        else
6349            return msl->Content[0].c_str();
6350    }
6351
6352    if(entry > 0)
6353        sLog.outErrorDb("Entry %i not found in `trinity_string` table.",entry);
6354    else
6355        sLog.outErrorDb("Trinity string entry %i not found in DB.",entry);
6356    return "<error>";
6357}
6358
6359void ObjectMgr::LoadSpellDisabledEntrys()
6360{
6361    m_DisabledPlayerSpells.clear();                                // need for reload case
6362    m_DisabledCreatureSpells.clear();
6363    QueryResult *result = WorldDatabase.Query("SELECT entry, disable_mask FROM spell_disabled");
6364
6365    uint32 total_count = 0;
6366
6367    if( !result )
6368    {
6369        barGoLink bar( 1 );
6370        bar.step();
6371
6372        sLog.outString();
6373        sLog.outString( ">> Loaded %u disabled spells", total_count );
6374        return;
6375    }
6376
6377    barGoLink bar( result->GetRowCount() );
6378
6379    Field* fields;
6380    do
6381    {
6382        bar.step();
6383        fields = result->Fetch();
6384        uint32 spellid = fields[0].GetUInt32();
6385        if(!sSpellStore.LookupEntry(spellid))
6386        {
6387            sLog.outErrorDb("Spell entry %u from `spell_disabled` doesn't exist in dbc, ignoring.",spellid);
6388            continue;
6389        }
6390        uint32 disable_mask = fields[1].GetUInt32();
6391        if(disable_mask & SPELL_DISABLE_PLAYER)
6392            m_DisabledPlayerSpells.insert(spellid);
6393        if(disable_mask & SPELL_DISABLE_CREATURE)
6394            m_DisabledCreatureSpells.insert(spellid);
6395        ++total_count;
6396   } while ( result->NextRow() );
6397
6398    delete result;
6399
6400    sLog.outString();
6401    sLog.outString( ">> Loaded %u disabled spells from `spell_disabled`", total_count);
6402}
6403
6404void ObjectMgr::LoadFishingBaseSkillLevel()
6405{
6406    mFishingBaseForArea.clear();                            // for relaod case
6407
6408    uint32 count = 0;
6409    QueryResult *result = WorldDatabase.Query("SELECT entry,skill FROM skill_fishing_base_level");
6410
6411    if( !result )
6412    {
6413        barGoLink bar( 1 );
6414
6415        bar.step();
6416
6417        sLog.outString();
6418        sLog.outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
6419        return;
6420    }
6421
6422    barGoLink bar( result->GetRowCount() );
6423
6424    do
6425    {
6426        bar.step();
6427
6428        Field *fields = result->Fetch();
6429        uint32 entry  = fields[0].GetUInt32();
6430        int32 skill   = fields[1].GetInt32();
6431
6432        AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
6433        if(!fArea)
6434        {
6435            sLog.outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist",entry);
6436            continue;
6437        }
6438
6439        mFishingBaseForArea[entry] = skill;
6440        ++count;
6441    }
6442    while (result->NextRow());
6443
6444    delete result;
6445
6446    sLog.outString();
6447    sLog.outString( ">> Loaded %u areas for fishing base skill level", count );
6448}
6449
6450// Searches for the same condition already in Conditions store
6451// Returns Id if found, else adds it to Conditions and returns Id
6452uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 value2 )
6453{
6454    PlayerCondition lc = PlayerCondition(condition, value1, value2);
6455    for (uint16 i=0; i < mConditions.size(); ++i)
6456    {
6457        if (lc == mConditions[i])
6458            return i;
6459    }
6460
6461    mConditions.push_back(lc);
6462
6463    if(mConditions.size() > 0xFFFF)
6464    {
6465        sLog.outError("Conditions store overflow! Current and later loaded conditions will ignored!");
6466        return 0;
6467    }
6468
6469    return mConditions.size() - 1;
6470}
6471
6472bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& names )
6473{
6474    for(int i =0; i < MAX_DECLINED_NAME_CASES; ++i)
6475    {
6476        std::wstring wname;
6477        if(!Utf8toWStr(names.name[i],wname))
6478            return false;
6479
6480        if(mainpart!=GetMainPartOfName(wname,i+1))
6481            return false;
6482    }
6483    return true;
6484}
6485
6486const char* ObjectMgr::GetAreaTriggerScriptName(uint32 id)
6487{
6488    AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(id);
6489    if(i!= mAreaTriggerScripts.end())
6490        return i->second.c_str();
6491    return "";
6492}
6493
6494// Checks if player meets the condition
6495bool PlayerCondition::Meets(Player const * player) const
6496{
6497    if( !player )
6498        return false;                                       // player not present, return false
6499
6500    switch (condition)
6501    {
6502        case CONDITION_NONE:
6503            return true;                                    // empty condition, always met
6504        case CONDITION_AURA:
6505            return player->HasAura(value1, value2);
6506        case CONDITION_ITEM:
6507            return player->HasItemCount(value1, value2);
6508        case CONDITION_ITEM_EQUIPPED:
6509            return player->GetItemOrItemWithGemEquipped(value1) != NULL;
6510        case CONDITION_ZONEID:
6511            return player->GetZoneId() == value1;
6512        case CONDITION_REPUTATION_RANK:
6513        {
6514            FactionEntry const* faction = sFactionStore.LookupEntry(value1);
6515            return faction && player->GetReputationRank(faction) >= value2;
6516        }
6517        case CONDITION_TEAM:
6518            return player->GetTeam() == value1;
6519        case CONDITION_SKILL:
6520            return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2;
6521        case CONDITION_QUESTREWARDED:
6522            return player->GetQuestRewardStatus(value1);
6523        case CONDITION_QUESTTAKEN:
6524        {
6525            QuestStatus status = player->GetQuestStatus(value1);
6526            return (status == QUEST_STATUS_INCOMPLETE);
6527        }
6528        case CONDITION_AD_COMMISSION_AURA:
6529        {
6530            Unit::AuraMap const& auras = player->GetAuras();
6531            for(Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
6532                if((itr->second->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetSpellProto()->SpellVisual==3580)
6533                    return true;
6534            return false;
6535        }
6536        default:
6537            return false;
6538    }
6539}
6540
6541// Verification of condition values validity
6542bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2)
6543{
6544    if( condition >= MAX_CONDITION)                         // Wrong condition type
6545    {
6546        sLog.outErrorDb("Condition has bad type of %u, skipped ", condition );
6547        return false;
6548    }
6549
6550    switch (condition)
6551    {
6552        case CONDITION_AURA:
6553        {
6554            if(!sSpellStore.LookupEntry(value1))
6555            {
6556                sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1);
6557                return false;
6558            }
6559            if(value2 > 2)
6560            {
6561                sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2);
6562                return false;
6563            }
6564            break;
6565        }
6566        case CONDITION_ITEM:
6567        {
6568            ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
6569            if(!proto)
6570            {
6571                sLog.outErrorDb("Item condition requires to have non existing item (%u), skipped", value1);
6572                return false;
6573            }
6574            break;
6575        }
6576        case CONDITION_ITEM_EQUIPPED:
6577        {
6578            ItemPrototype const *proto = objmgr.GetItemPrototype(value1);
6579            if(!proto)
6580            {
6581                sLog.outErrorDb("ItemEquipped condition requires to have non existing item (%u) equipped, skipped", value1);
6582                return false;
6583            }
6584            break;
6585        }
6586        case CONDITION_ZONEID:
6587        {
6588            AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1);
6589            if(!areaEntry)
6590            {
6591                sLog.outErrorDb("Zone condition requires to be in non existing area (%u), skipped", value1);
6592                return false;
6593            }
6594            if(areaEntry->zone != 0)
6595            {
6596                sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", value1);
6597                return false;
6598            }
6599            break;
6600        }
6601        case CONDITION_REPUTATION_RANK:
6602        {
6603            FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1);
6604            if(!factionEntry)
6605            {
6606                sLog.outErrorDb("Reputation condition requires to have reputation non existing faction (%u), skipped", value1);
6607                return false;
6608            }
6609            break;
6610        }
6611        case CONDITION_TEAM:
6612        {
6613            if (value1 != ALLIANCE && value1 != HORDE)
6614            {
6615                sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", value1);
6616                return false;
6617            }
6618            break;
6619        }
6620        case CONDITION_SKILL:
6621        {
6622            SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1);
6623            if (!pSkill)
6624            {
6625                sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", value1);
6626                return false;
6627            }
6628            if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue() )
6629            {
6630                sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", value2);
6631                return false;
6632            }
6633            break;
6634        }
6635        case CONDITION_QUESTREWARDED:
6636        case CONDITION_QUESTTAKEN:
6637        {
6638            Quest const *Quest = objmgr.GetQuestTemplate(value1);
6639            if (!Quest)
6640            {
6641                sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", value1);
6642                return false;
6643            }
6644            if(value2)
6645                sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
6646            break;
6647        }
6648        case CONDITION_AD_COMMISSION_AURA:
6649        {
6650            if(value1)
6651                sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", value1);
6652            if(value2)
6653                sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2);
6654            break;
6655        }
6656    }
6657    return true;
6658}
6659
6660SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
6661{
6662    switch(pSkill->categoryId)
6663    {
6664        case SKILL_CATEGORY_LANGUAGES: return SKILL_RANGE_LANGUAGE;
6665        case SKILL_CATEGORY_WEAPON:
6666            if(pSkill->id!=SKILL_FIST_WEAPONS)
6667                return SKILL_RANGE_LEVEL;
6668            else
6669                return SKILL_RANGE_MONO;
6670        case SKILL_CATEGORY_ARMOR:
6671        case SKILL_CATEGORY_CLASS:
6672            if(pSkill->id != SKILL_POISONS && pSkill->id != SKILL_LOCKPICKING)
6673                return SKILL_RANGE_MONO;
6674            else
6675                return SKILL_RANGE_LEVEL;
6676        case SKILL_CATEGORY_SECONDARY:
6677        case SKILL_CATEGORY_PROFESSION:
6678            // not set skills for professions and racial abilities
6679            if(IsProfessionSkill(pSkill->id))
6680                return SKILL_RANGE_RANK;
6681            else if(racial)
6682                return SKILL_RANGE_NONE;
6683            else
6684                return SKILL_RANGE_MONO;
6685        default:
6686        case SKILL_CATEGORY_ATTRIBUTES:                     //not found in dbc
6687        case SKILL_CATEGORY_NOT_DISPLAYED:                  //only GENEREC(DND)
6688            return SKILL_RANGE_NONE;
6689    }
6690}
6691
6692void ObjectMgr::LoadGameTele()
6693{
6694    m_GameTeleMap.clear();                                  // for relaod case
6695
6696    uint32 count = 0;
6697    QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
6698
6699    if( !result )
6700    {
6701        barGoLink bar( 1 );
6702
6703        bar.step();
6704
6705        sLog.outString();
6706        sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
6707        return;
6708    }
6709
6710    barGoLink bar( result->GetRowCount() );
6711
6712    do
6713    {
6714        bar.step();
6715
6716        Field *fields = result->Fetch();
6717
6718        uint32 id         = fields[0].GetUInt32();
6719
6720        GameTele gt;
6721
6722        gt.position_x     = fields[1].GetFloat();
6723        gt.position_y     = fields[2].GetFloat();
6724        gt.position_z     = fields[3].GetFloat();
6725        gt.orientation    = fields[4].GetFloat();
6726        gt.mapId          = fields[5].GetUInt32();
6727        gt.name           = fields[6].GetCppString();
6728
6729        if(!MapManager::IsValidMapCoord(gt.mapId,gt.position_x,gt.position_y,gt.position_z,gt.orientation))
6730        {
6731            sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.",id,gt.name.c_str());
6732            continue;
6733        }
6734
6735        if(!Utf8toWStr(gt.name,gt.wnameLow))
6736        {
6737            sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.",id);
6738            continue;
6739        }
6740
6741        wstrToLower( gt.wnameLow );
6742
6743        m_GameTeleMap[id] = gt;
6744
6745        ++count;
6746    }
6747    while (result->NextRow());
6748
6749    delete result;
6750
6751    sLog.outString();
6752    sLog.outString( ">> Loaded %u game tele's", count );
6753}
6754
6755GameTele const* ObjectMgr::GetGameTele(std::string name) const
6756{
6757    // explicit name case
6758    std::wstring wname;
6759    if(!Utf8toWStr(name,wname))
6760        return false;
6761
6762    // converting string that we try to find to lower case
6763    wstrToLower( wname );
6764
6765    for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
6766        if(itr->second.wnameLow == wname)
6767            return &itr->second;
6768
6769    return NULL;
6770}
6771
6772bool ObjectMgr::AddGameTele(GameTele& tele)
6773{
6774    // find max id
6775    uint32 new_id = 0;
6776    for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
6777        if(itr->first > new_id)
6778            new_id = itr->first;
6779   
6780    // use next
6781    ++new_id;
6782
6783    if(!Utf8toWStr(tele.name,tele.wnameLow))
6784        return false;
6785
6786    wstrToLower( tele.wnameLow );
6787
6788    m_GameTeleMap[new_id] = tele;
6789
6790    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')",
6791        new_id,tele.position_x,tele.position_y,tele.position_z,tele.orientation,tele.mapId,tele.name.c_str());
6792}
6793
6794bool ObjectMgr::DeleteGameTele(std::string name)
6795{
6796    // explicit name case
6797    std::wstring wname;
6798    if(!Utf8toWStr(name,wname))
6799        return false;
6800
6801    // converting string that we try to find to lower case
6802    wstrToLower( wname );
6803
6804    for(GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
6805    {
6806        if(itr->second.wnameLow == wname)
6807        {
6808            WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",itr->second.name.c_str());
6809            m_GameTeleMap.erase(itr);
6810            return true;
6811        }
6812    }
6813
6814    return false;
6815}
6816
6817void ObjectMgr::LoadTrainerSpell()
6818{
6819    // For reload case
6820    for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
6821        itr->second.Clear();
6822    m_mCacheTrainerSpellMap.clear();
6823
6824    std::set<uint32> skip_trainers;
6825
6826    QueryResult *result = WorldDatabase.PQuery("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM npc_trainer");
6827
6828    if( !result )
6829    {
6830        barGoLink bar( 1 );
6831
6832        bar.step();
6833
6834        sLog.outString();
6835        sLog.outErrorDb(">> Loaded `npc_trainer`, table is empty!");
6836        return;
6837    }
6838
6839    barGoLink bar( result->GetRowCount() );
6840
6841    uint32 count = 0;
6842    do
6843    {
6844        bar.step();
6845
6846        Field* fields = result->Fetch();
6847
6848        uint32 entry  = fields[0].GetUInt32();
6849        uint32 spell  = fields[1].GetUInt32();
6850
6851        CreatureInfo const* cInfo = GetCreatureTemplate(entry);
6852
6853        if(!cInfo)
6854        {
6855            sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
6856            continue;
6857        }
6858
6859        if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
6860        {
6861            if(skip_trainers.count(entry) == 0)
6862            {
6863                sLog.outErrorDb("Table `npc_trainer` have data for not creature template (Entry: %u) without trainer flag, ignore", entry);
6864                skip_trainers.insert(entry);
6865            }
6866            continue;
6867        }
6868
6869        SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
6870        if(!spellinfo)
6871        {
6872            sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u ) has non existing spell %u, ignore", entry,spell);
6873            continue;
6874        }
6875
6876        if(!SpellMgr::IsSpellValid(spellinfo))
6877        {
6878            sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u) has broken learning spell %u, ignore", entry, spell);
6879            continue;
6880        }
6881
6882        TrainerSpell* pTrainerSpell = new TrainerSpell();
6883        pTrainerSpell->spell         = spell;
6884        pTrainerSpell->spellcost     = fields[2].GetUInt32();
6885        pTrainerSpell->reqskill      = fields[3].GetUInt32();
6886        pTrainerSpell->reqskillvalue = fields[4].GetUInt32();
6887        pTrainerSpell->reqlevel      = fields[5].GetUInt32();
6888
6889        if(!pTrainerSpell->reqlevel)
6890            pTrainerSpell->reqlevel = spellinfo->spellLevel;
6891
6892
6893        TrainerSpellData& data = m_mCacheTrainerSpellMap[entry];
6894
6895        if(SpellMgr::IsProfessionSpell(spell))
6896            data.trainerType = 2;
6897
6898        data.spellList.push_back(pTrainerSpell);
6899        ++count;
6900
6901    } while (result->NextRow());
6902    delete result;
6903
6904    sLog.outString();
6905    sLog.outString( ">> Loaded Trainers %d", count );
6906}
6907
6908void ObjectMgr::LoadVendors()
6909{
6910    // For reload case
6911    for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
6912        itr->second.Clear();
6913    m_mCacheVendorItemMap.clear();
6914
6915    std::set<uint32> skip_vendors;
6916
6917    QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor");
6918    if( !result )
6919    {
6920        barGoLink bar( 1 );
6921
6922        bar.step();
6923
6924        sLog.outString();
6925        sLog.outErrorDb(">> Loaded `npc_vendor`, table is empty!");
6926        return;
6927    }
6928
6929    barGoLink bar( result->GetRowCount() );
6930
6931    uint32 count = 0;
6932    do
6933    {
6934        bar.step();
6935        Field* fields = result->Fetch();
6936
6937        uint32 entry        = fields[0].GetUInt32();
6938        uint32 item_id      = fields[1].GetUInt32();
6939        uint32 maxcount     = fields[2].GetUInt32();
6940        uint32 incrtime     = fields[3].GetUInt32();
6941        uint32 ExtendedCost = fields[4].GetUInt32();
6942
6943        if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost,NULL,&skip_vendors))
6944            continue;
6945
6946        VendorItemData& vList = m_mCacheVendorItemMap[entry];
6947
6948        vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
6949        ++count;
6950
6951    } while (result->NextRow());
6952    delete result;
6953
6954    sLog.outString();
6955    sLog.outString( ">> Loaded %d Vendors ", count );
6956}
6957
6958void ObjectMgr::LoadNpcTextId()
6959{
6960
6961    m_mCacheNpcTextIdMap.clear();
6962
6963    QueryResult* result = WorldDatabase.PQuery("SELECT npc_guid, textid FROM npc_gossip");
6964    if( !result )
6965    {
6966        barGoLink bar( 1 );
6967
6968        bar.step();
6969
6970        sLog.outString();
6971        sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
6972        return;
6973    }
6974
6975    barGoLink bar( result->GetRowCount() );
6976
6977    uint32 count = 0;
6978    uint32 guid,textid;
6979    do
6980    {
6981        bar.step();
6982
6983        Field* fields = result->Fetch();
6984
6985        guid   = fields[0].GetUInt32();
6986        textid = fields[1].GetUInt32();
6987
6988        if (!GetCreatureData(guid))
6989        {
6990            sLog.outErrorDb("Table `npc_gossip` have not existed creature (GUID: %u) entry, ignore. ",guid);
6991            continue;
6992        }
6993        if (!GetGossipText(textid))
6994        {
6995            sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
6996            continue;
6997        }
6998
6999        m_mCacheNpcTextIdMap[guid] = textid ;
7000        ++count;
7001
7002    } while (result->NextRow());
7003    delete result;
7004
7005    sLog.outString();
7006    sLog.outString( ">> Loaded %d NpcTextId ", count );
7007}
7008
7009void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost, bool savetodb)
7010{
7011    VendorItemData& vList = m_mCacheVendorItemMap[entry];
7012    vList.AddItem(item,maxcount,incrtime,extendedcost);
7013
7014    if(savetodb) WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
7015}
7016
7017bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item, bool savetodb)
7018{
7019    CacheVendorItemMap::iterator  iter = m_mCacheVendorItemMap.find(entry);
7020    if(iter == m_mCacheVendorItemMap.end())
7021        return false;
7022
7023    if(!iter->second.FindItem(item))
7024        return false;
7025
7026    iter->second.RemoveItem(item);
7027    if(savetodb) WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
7028    return true;
7029}
7030
7031bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set<uint32>* skip_vendors, uint32 ORnpcflag ) const
7032{
7033    CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
7034    if(!cInfo)
7035    {
7036        if(pl)
7037            ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
7038        else
7039            sLog.outErrorDb("Table `(game_event_)npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
7040        return false;
7041    }
7042
7043    if(!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
7044    {
7045        if(!skip_vendors || skip_vendors->count(vendor_entry)==0)
7046        {
7047            if(pl)
7048                ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
7049            else
7050                sLog.outErrorDb("Table `(game_event_)npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
7051
7052            if(skip_vendors)
7053                skip_vendors->insert(vendor_entry);
7054        }
7055        return false;
7056    }
7057
7058    if(!GetItemPrototype(item_id))
7059    {
7060        if(pl)
7061            ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
7062        else
7063            sLog.outErrorDb("Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
7064        return false;
7065    }
7066
7067    if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
7068    {
7069        if(pl)
7070            ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST,ExtendedCost);
7071        else
7072            sLog.outErrorDb("Table `(game_event_)npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
7073        return false;
7074    }
7075
7076    if(maxcount > 0 && incrtime == 0)
7077    {
7078        if(pl)
7079            ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
7080        else
7081            sLog.outErrorDb( "Table `(game_event_)npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
7082        return false;
7083    }
7084    else if(maxcount==0 && incrtime > 0)
7085    {
7086        if(pl)
7087            ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
7088        else
7089            sLog.outErrorDb( "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
7090        return false;
7091    }
7092
7093    VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
7094    if(!vItems)
7095        return true;                                        // later checks for non-empty lists
7096
7097    if(vItems->FindItem(item_id))
7098    {
7099        if(pl)
7100            ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,item_id);
7101        else
7102            sLog.outErrorDb( "Table `(game_event_)npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
7103        return false;
7104    }
7105
7106    if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
7107    {
7108        if(pl)
7109            ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
7110        else
7111            sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
7112        return false;
7113    }
7114
7115    return true;
7116}
7117
7118// Functions for scripting access
7119const char* GetAreaTriggerScriptNameById(uint32 id)
7120{
7121    return objmgr.GetAreaTriggerScriptName(id);
7122}
7123
7124bool LoadTrinityStrings(DatabaseType& db, char const* table,int32 start_value, int32 end_value)
7125{
7126    if(start_value >= 0 || start_value <= end_value)        // start/end reversed for negative values
7127    {
7128        sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
7129        start_value = -1;
7130        end_value = std::numeric_limits<int32>::min();
7131    }
7132
7133    // for scripting localized strings allowed use _only_ negative entries
7134    return objmgr.LoadTrinityStrings(db,table,end_value,start_value);
7135}
Note: See TracBrowser for help on using the browser.