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

Revision 144, 253.7 kB (checked in by yumileroy, 17 years ago)

[svn] Disable combat_reach spams.
Fix holy nova. Patch provided by Disassembler.

Original author: megamage
Date: 2008-11-01 18:52:59-05:00

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