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

Revision 79, 248.2 kB (checked in by yumileroy, 17 years ago)

[svn] * Minor code fixes
* Move account related functions from ObjectMgr? to AccountMgr? and drop duplicate functions - source mangos
* recognize the dummy spells 38637, 38638 and 38639 as negative - source mangos
* added new command ".reload all_locales". Now all locales_* tables can be reloaded - source mangos

Original author: KingPin?
Date: 2008-10-20 12:59:45-05:00

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