/*
 * Copyright (C) 2005 - 2012 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008 - 2012 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010 - 2012 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "gamePCH.h"
#include "Common.h"
#include "DatabaseEnv.h"
#include "SQLStorage.h"
#include "SQLStorageImpl.h"
#include "Log.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
#include "UpdateMask.h"
#include "World.h"
#include "Group.h"
#include "Guild.h"
#include "ArenaTeam.h"
#include "Transport.h"
#include "Language.h"
#include "GameEventMgr.h"
#include "Spell.h"
#include "Chat.h"
#include "AccountMgr.h"
#include "InstanceSaveMgr.h"
#include "SpellAuras.h"
#include "Util.h"
#include "WaypointManager.h"
#include "GossipDef.h"
#include "Vehicle.h"
#include "AchievementMgr.h"
#include "DisableMgr.h"
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "PoolMgr.h"
#include "DB2Structure.h"
#include "DB2Stores.h"

ScriptMapMap sQuestEndScripts;
ScriptMapMap sQuestStartScripts;
ScriptMapMap sSpellScripts;
ScriptMapMap sGameObjectScripts;
ScriptMapMap sEventScripts;
ScriptMapMap sGossipScripts;
ScriptMapMap sWaypointScripts;

std::string GetScriptsTableNameByType (ScriptsType type)
{
    std::string res = "";
    switch (type)
    {
    case SCRIPTS_QUEST_END:
        res = "quest_end_scripts";
        break;
    case SCRIPTS_QUEST_START:
        res = "quest_start_scripts";
        break;
    case SCRIPTS_SPELL:
        res = "spell_scripts";
        break;
    case SCRIPTS_GAMEOBJECT:
        res = "gameobject_scripts";
        break;
    case SCRIPTS_EVENT:
        res = "event_scripts";
        break;
    case SCRIPTS_WAYPOINT:
        res = "waypoint_scripts";
        break;
    case SCRIPTS_GOSSIP:
        res = "gossip_scripts";
        break;
    default:
        break;
    }
    return res;
}

ScriptMapMap* GetScriptsMapByType (ScriptsType type)
{
    ScriptMapMap* res = NULL;
    switch (type)
    {
    case SCRIPTS_QUEST_END:
        res = &sQuestEndScripts;
        break;
    case SCRIPTS_QUEST_START:
        res = &sQuestStartScripts;
        break;
    case SCRIPTS_SPELL:
        res = &sSpellScripts;
        break;
    case SCRIPTS_GAMEOBJECT:
        res = &sGameObjectScripts;
        break;
    case SCRIPTS_EVENT:
        res = &sEventScripts;
        break;
    case SCRIPTS_WAYPOINT:
        res = &sWaypointScripts;
        break;
    case SCRIPTS_GOSSIP:
        res = &sGossipScripts;
        break;
    default:
        break;
    }
    return res;
}

std::string GetScriptCommandName (ScriptCommands command)
{
    std::string res = "";
    switch (command)
    {
    case SCRIPT_COMMAND_TALK:
        res = "SCRIPT_COMMAND_TALK";
        break;
    case SCRIPT_COMMAND_EMOTE:
        res = "SCRIPT_COMMAND_EMOTE";
        break;
    case SCRIPT_COMMAND_FIELD_SET:
        res = "SCRIPT_COMMAND_FIELD_SET";
        break;
    case SCRIPT_COMMAND_MOVE_TO:
        res = "SCRIPT_COMMAND_MOVE_TO";
        break;
    case SCRIPT_COMMAND_FLAG_SET:
        res = "SCRIPT_COMMAND_FLAG_SET";
        break;
    case SCRIPT_COMMAND_FLAG_REMOVE:
        res = "SCRIPT_COMMAND_FLAG_REMOVE";
        break;
    case SCRIPT_COMMAND_TELEPORT_TO:
        res = "SCRIPT_COMMAND_TELEPORT_TO";
        break;
    case SCRIPT_COMMAND_QUEST_EXPLORED:
        res = "SCRIPT_COMMAND_QUEST_EXPLORED";
        break;
    case SCRIPT_COMMAND_KILL_CREDIT:
        res = "SCRIPT_COMMAND_KILL_CREDIT";
        break;
    case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
        res = "SCRIPT_COMMAND_RESPAWN_GAMEOBJECT";
        break;
    case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
        res = "SCRIPT_COMMAND_TEMP_SUMMON_CREATURE";
        break;
    case SCRIPT_COMMAND_OPEN_DOOR:
        res = "SCRIPT_COMMAND_OPEN_DOOR";
        break;
    case SCRIPT_COMMAND_CLOSE_DOOR:
        res = "SCRIPT_COMMAND_CLOSE_DOOR";
        break;
    case SCRIPT_COMMAND_ACTIVATE_OBJECT:
        res = "SCRIPT_COMMAND_ACTIVATE_OBJECT";
        break;
    case SCRIPT_COMMAND_REMOVE_AURA:
        res = "SCRIPT_COMMAND_REMOVE_AURA";
        break;
    case SCRIPT_COMMAND_CAST_SPELL:
        res = "SCRIPT_COMMAND_CAST_SPELL";
        break;
    case SCRIPT_COMMAND_PLAY_SOUND:
        res = "SCRIPT_COMMAND_PLAY_SOUND";
        break;
    case SCRIPT_COMMAND_CREATE_ITEM:
        res = "SCRIPT_COMMAND_CREATE_ITEM";
        break;
    case SCRIPT_COMMAND_DESPAWN_SELF:
        res = "SCRIPT_COMMAND_DESPAWN_SELF";
        break;
    case SCRIPT_COMMAND_LOAD_PATH:
        res = "SCRIPT_COMMAND_LOAD_PATH";
        break;
    case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT:
        res = "SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT";
        break;
    case SCRIPT_COMMAND_KILL:
        res = "SCRIPT_COMMAND_KILL";
        break;
        // TrinityCore only
    case SCRIPT_COMMAND_ORIENTATION:
        res = "SCRIPT_COMMAND_ORIENTATION";
        break;
    case SCRIPT_COMMAND_EQUIP:
        res = "SCRIPT_COMMAND_EQUIP";
        break;
    case SCRIPT_COMMAND_MODEL:
        res = "SCRIPT_COMMAND_MODEL";
        break;
    case SCRIPT_COMMAND_CLOSE_GOSSIP:
        res = "SCRIPT_COMMAND_CLOSE_GOSSIP";
        break;
    case SCRIPT_COMMAND_PLAYMOVIE:
        res = "SCRIPT_COMMAND_PLAYMOVIE";
        break;
    default:
    {
        char sz[32];
        sprintf(sz, "Unknown command: %u", command);
        res = sz;
        break;
    }
    }
    return res;
}

std::string ScriptInfo::GetDebugInfo () const
{
    char sz[256];
    sprintf(sz, "%s ('%s' script id: %u)", GetScriptCommandName(command).c_str(), GetScriptsTableNameByType(type).c_str(), id);
    return std::string(sz);
}

bool normalizePlayerName (std::string& name)
{
    if (name.empty())
        return false;

    wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME + 1];
    size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;

    if (!Utf8toWStr(name, &wstr_buf[0], wstr_len))
        return false;

    wstr_buf[0] = wcharToUpper(wstr_buf[0]);
    for (size_t i = 1; i < wstr_len; ++i)
        wstr_buf[i] = wcharToLower(wstr_buf[i]);

    if (!WStrToUtf8(wstr_buf, wstr_len, name))
        return false;

    return true;
}

LanguageDesc lang_description[LANGUAGES_COUNT] =
{
{ LANG_ADDON, 0, 0 },
{ LANG_UNIVERSAL, 0, 0 },
{ LANG_ORCISH, 669, SKILL_LANG_ORCISH },
{ LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
{ LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
{ LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
{ LANG_COMMON, 668, SKILL_LANG_COMMON },
{ LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
{ LANG_TITAN, 816, SKILL_LANG_TITAN },
{ LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
{ LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
{ LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
{ LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
{ LANG_TROLL, 7341, SKILL_LANG_TROLL },
{ LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
{ LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
{ LANG_ZOMBIE, 0, 0 },
{ LANG_GNOMISH_BINARY, 0, 0 },
{ LANG_GOBLIN_BINARY, 0, 0 },
{ LANG_WORGEN, 69270, SKILL_LANG_WORGEN },
{ LANG_GOBLIN, 69269, SKILL_LANG_GOBLIN } };

LanguageDesc const* GetLanguageDescByID (uint32 lang)
{
    for (uint8 i = 0; i < LANGUAGES_COUNT; ++i)
    {
        if (uint32(lang_description[i].lang_id) == lang)
            return &lang_description[i];
    }

    return NULL;
}

bool SpellClickInfo::IsFitToRequirements (Player const* player, Creature const * clickNpc) const
{
    if (questStart)
    {
        // not in expected required quest state
        if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)))
            return false;
    }

    if (questEnd)
    {
        // not in expected forbidden quest state
        if (!player || player->GetQuestRewardStatus(questEnd))
            return false;
    }

    if (auraRequired)
        if (!player->HasAura(auraRequired))
            return false;

    if (auraForbidden)
        if (player->HasAura(auraForbidden))
            return false;

    Unit const * summoner = NULL;
    // Check summoners for party
    if (clickNpc->isSummon())
        summoner = clickNpc->ToTempSummon()->GetSummoner();
    if (!summoner)
        summoner = clickNpc;

    switch (userType)
    {
    case SPELL_CLICK_USER_FRIEND:
        if (!player->IsFriendlyTo(summoner))
            return false;
        break;
    case SPELL_CLICK_USER_RAID:
        if (!player->IsInRaidWith(summoner))
            return false;
        break;
    case SPELL_CLICK_USER_PARTY:
        if (!player->IsInPartyWith(summoner))
            return false;
        break;
    default:
        break;
    }

    return true;
}

ObjectMgr::ObjectMgr ()
{
    m_hiCharGuid = 1;
    m_hiCreatureGuid = 1;
    m_hiPetGuid = 1;
    m_hiVehicleGuid = 1;
    m_hiItemGuid = 1;
    m_hiGoGuid = 1;
    m_hiDoGuid = 1;
    m_hiCorpseGuid = 1;
    m_hiPetNumber = 1;
    m_hiGroupGuid = 1;
    m_hiMoTransGuid = 1;
    m_ItemTextId = 1;
    m_mailid = 1;
    m_equipmentSetGuid = 1;
    m_guildId = 1;
    m_arenaTeamId = 1;
    m_auctionid = 1;
}

ObjectMgr::~ObjectMgr ()
{
    for (QuestMap::iterator i = mQuestTemplates.begin(); i != mQuestTemplates.end(); ++i)
        delete i->second;

    for (PetLevelInfoMap::iterator i = petInfo.begin(); i != petInfo.end(); ++i)
        delete[] i->second;

    // free only if loaded
    for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
        delete[] playerClassInfo[class_].levelInfo;

    for (int race = 0; race < MAX_RACES; ++race)
        for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
            delete[] playerInfo[race][class_].levelInfo;

    // free group and guild objects
    for (GroupSet::iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
        delete *itr;

    for (GuildMap::iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
        if (*itr)
            delete *itr;

    for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
        delete itr->second;

    for (GuildRewardsVector::iterator itr = mGuildRewards.begin(); itr != mGuildRewards.end(); ++itr)
        delete (*itr);

    for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
        itr->second.Clear();

    m_mCacheTrainerSpellMap.clear();
}

Group * ObjectMgr::GetGroupByGUID (uint32 guid) const
{
    for (GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
        if ((*itr)->GetLowGUID() == guid)
            return *itr;

    return NULL;
}

// Guild collection
Guild* ObjectMgr::GetGuildById (uint32 guildId) const
{
    if (guildId == 0)
        return NULL;

    // Make sure given index exists in collection
    if (guildId < uint32(mGuildMap.size()))
        return mGuildMap[guildId];
    return NULL;
}

Guild* ObjectMgr::GetGuildByName (const std::string& guildname) const
{
    std::string search = guildname;
    std::transform(search.begin(), search.end(), search.begin(), ::toupper);
    for (GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
    {
        if (*itr)
        {
            std::string gname = (*itr)->GetName();
            std::transform(gname.begin(), gname.end(), gname.begin(), ::toupper);
            if (search == gname)
                return *itr;
        }
    }
    return NULL;
}

std::string ObjectMgr::GetGuildNameById (uint32 guildId) const
{
    if (Guild* pGuild = GetGuildById(guildId))
        return pGuild->GetName();
    return "";
}

Guild* ObjectMgr::GetGuildByLeader (const uint64 &guid) const
{
    for (GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
        if ((*itr) && (*itr)->GetLeaderGUID() == guid)
            return *itr;

    return NULL;
}

void ObjectMgr::AddGuild (Guild* pGuild)
{
    uint32 guildId = pGuild->GetId();
    // Allocate space if necessary
    if (guildId >= uint32(mGuildMap.size()))
        // Reserve a bit more space than necessary.
        // 16 is intentional and it will allow creation of next 16 guilds happen
        // without reallocation.
        mGuildMap.resize(guildId + 16);
    mGuildMap[guildId] = pGuild;
}

void ObjectMgr::RemoveGuild (uint32 guildId)
{
    // Make sure given index exists
    if (guildId < uint32(mGuildMap.size()))
        mGuildMap[guildId] = NULL;
}

// Arena teams collection
ArenaTeam* ObjectMgr::GetArenaTeamById (uint32 arenateamid) const
{
    ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid);
    if (itr != mArenaTeamMap.end())
        return itr->second;

    return NULL;
}

ArenaTeam* ObjectMgr::GetArenaTeamByName (const std::string& arenateamname) const
{
    std::string search = arenateamname;
    std::transform(search.begin(), search.end(), search.begin(), ::toupper);
    for (ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
    {
        std::string teamname = itr->second->GetName();
        std::transform(teamname.begin(), teamname.end(), teamname.begin(), ::toupper);
        if (search == teamname)
            return itr->second;
    }
    return NULL;
}

ArenaTeam* ObjectMgr::GetArenaTeamByCaptain (uint64 const& guid) const
{
    for (ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
        if (itr->second->GetCaptain() == guid)
            return itr->second;

    return NULL;
}

void ObjectMgr::AddArenaTeam (ArenaTeam* arenaTeam)
{
    mArenaTeamMap[arenaTeam->GetId()] = arenaTeam;
}

void ObjectMgr::RemoveArenaTeam (uint32 Id)
{
    mArenaTeamMap.erase(Id);
}

void ObjectMgr::AddLocaleString(std::string const& s, LocaleConstant locale, StringVector& data)
{
    if (!s.empty())
    {
        if (data.size() <= size_t(locale))
            data.resize(locale + 1);

        data[locale] = s;
    }
}

void ObjectMgr::LoadCreatureLocales ()
{
    mCreatureLocaleMap.clear();          // need for reload case

    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");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        CreatureLocale& data = mCreatureLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            LocaleConstant locale = (LocaleConstant) i;
            std::string str = fields[1 + 2 * (i - 1)].GetString();
            AddLocaleString(str, locale, data.Name);

            str = fields[1 + 2 * (i - 1) + 1].GetString();
            AddLocaleString(str, locale, data.SubName);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu creature locale strings", (unsigned long) mCreatureLocaleMap.size());
}

void ObjectMgr::LoadGossipMenuItemsLocales ()
{
    mGossipMenuItemsLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT menu_id, id, "
            "option_text_loc1, box_text_loc1, option_text_loc2, box_text_loc2, "
            "option_text_loc3, box_text_loc3, option_text_loc4, box_text_loc4, "
            "option_text_loc5, box_text_loc5, option_text_loc6, box_text_loc6, "
            "option_text_loc7, box_text_loc7, option_text_loc8, box_text_loc8 "
            "FROM locales_gossip_menu_option");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint16 menuId = fields[0].GetUInt16();
        uint16 id = fields[1].GetUInt16();

        GossipMenuItemsLocale& data = mGossipMenuItemsLocaleMap[MAKE_PAIR32(menuId, id)];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            LocaleConstant locale = (LocaleConstant) i;
            std::string str = fields[2 + 2 * (i - 1)].GetString();
            AddLocaleString(str, locale, data.OptionText);

            str = fields[2 + 2 * (i - 1) + 1].GetString();
            AddLocaleString(str, locale, data.BoxText);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu gossip_menu_option locale strings", (unsigned long) mGossipMenuItemsLocaleMap.size());
}

void ObjectMgr::LoadPointOfInterestLocales ()
{
    mPointOfInterestLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT entry, icon_name_loc1, icon_name_loc2, icon_name_loc3, icon_name_loc4, icon_name_loc5, icon_name_loc6, icon_name_loc7, icon_name_loc8 FROM locales_points_of_interest");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            std::string str = fields[i].GetString();
            AddLocaleString(str, LocaleConstant(i), data.IconName);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu points_of_interest locale strings", (unsigned long) mPointOfInterestLocaleMap.size());
}

struct SQLCreatureLoader: public SQLStorageLoaderBase<SQLCreatureLoader>
{
    template<class D>
    void convert_from_str (uint32 /*field_pos*/, char *src, D &dst)
    {
        dst = D(sObjectMgr->GetScriptId(src));
    }
};

void ObjectMgr::LoadCreatureTemplates ()
{
    SQLCreatureLoader loader;
    loader.Load(sCreatureStorage);

    sLog->outString(">> Loaded %u creature definitions", sCreatureStorage.RecordCount);
    sLog->outString();

    // check data correctness
    for (uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
    {
        CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
        CheckCreatureTemplate(cInfo);
    }
}

void ObjectMgr::CheckCreatureTemplate (CreatureInfo const* cInfo)
{
    if (!cInfo)
        return;

    bool ok = true;          // bool to allow continue outside this loop
    for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
    {
        if (!cInfo->DifficultyEntry[diff])
            continue;
        ok = false;          // will be set to true at the end of this loop again

        CreatureInfo const* difficultyInfo = GetCreatureTemplate(cInfo->DifficultyEntry[diff]);
        if (!difficultyInfo)
        {
            sLog->outErrorDb("Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u does not exist.", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
            continue;
        }

        bool ok2 = true;
        for (uint32 diff2 = 0; diff2 < MAX_DIFFICULTY - 1 && ok2; ++diff2)
        {
            ok2 = false;
            if (difficultyEntries[diff2].find(cInfo->Entry) != difficultyEntries[diff2].end())
            {
                sLog->outErrorDb("Creature (Entry: %u) is listed as `difficulty_entry_%u` of another creature, but itself lists %u in `difficulty_entry_%u`.", cInfo->Entry, diff2 + 1, cInfo->DifficultyEntry[diff], diff + 1);
                continue;
            }

            if (difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != difficultyEntries[diff2].end())
            {
                sLog->outErrorDb("Creature (Entry: %u) already listed as `difficulty_entry_%u` for another entry.", cInfo->DifficultyEntry[diff], diff2 + 1);
                continue;
            }

            if (hasDifficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != hasDifficultyEntries[diff2].end())
            {
                sLog->outErrorDb("Creature (Entry: %u) has `difficulty_entry_%u`=%u but creature entry %u has itself a value in `difficulty_entry_%u`.", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff], diff2 + 1);
                continue;
            }
            ok2 = true;
        }
        if (!ok2)
            continue;

        if (cInfo->unit_class != difficultyInfo->unit_class)
        {
            sLog->outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in difficulty %u mode (Entry: %u, class %u).", cInfo->Entry, cInfo->unit_class, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->unit_class);
            continue;
        }

        if (cInfo->npcflag != difficultyInfo->npcflag)
        {
            sLog->outErrorDb("Creature (Entry: %u) has different `npcflag` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        if (cInfo->trainer_class != difficultyInfo->trainer_class)
        {
            sLog->outErrorDb("Creature (Entry: %u) has different `trainer_class` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        if (cInfo->trainer_race != difficultyInfo->trainer_race)
        {
            sLog->outErrorDb("Creature (Entry: %u) has different `trainer_race` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        if (cInfo->trainer_type != difficultyInfo->trainer_type)
        {
            sLog->outErrorDb("Creature (Entry: %u) has different `trainer_type` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        if (cInfo->trainer_spell != difficultyInfo->trainer_spell)
        {
            sLog->outErrorDb("Creature (Entry: %u) has different `trainer_spell` in difficulty %u mode (Entry: %u).", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        if (difficultyInfo->AIName && *difficultyInfo->AIName)
        {
            sLog->outErrorDb("Creature (Entry: %u) lists difficulty %u mode entry %u with `AIName` filled in. `AIName` of difficulty 0 mode creature is always used instead.", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        if (difficultyInfo->ScriptID)
        {
            sLog->outErrorDb("Creature (Entry: %u) lists difficulty %u mode entry %u with `ScriptName` filled in. `ScriptName` of difficulty 0 mode creature is always used instead.", cInfo->Entry, diff + 1, cInfo->DifficultyEntry[diff]);
            continue;
        }

        hasDifficultyEntries[diff].insert(cInfo->Entry);
        difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
        ok = true;
    }

    FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_A);
    if (!factionTemplate)
        sLog->outErrorDb("Creature (Entry: %u) has non-existing faction_A template (%u).", cInfo->Entry, cInfo->faction_A);

    factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction_H);
    if (!factionTemplate)
        sLog->outErrorDb("Creature (Entry: %u) has non-existing faction_H template (%u).", cInfo->Entry, cInfo->faction_H);

    // used later for scale
    CreatureDisplayInfoEntry const* displayScaleEntry = NULL;

    if (cInfo->Modelid1)
    {
        CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
        if (!displayEntry)
        {
            sLog->outErrorDb("Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1);
            const_cast<CreatureInfo*>(cInfo)->Modelid1 = 0;
        }
        else if (!displayScaleEntry)
            displayScaleEntry = displayEntry;

        CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid1);
        if (!minfo)
            sLog->outErrorDb("No model data exist for `Modelid1` = %u listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry);
    }

    if (cInfo->Modelid2)
    {
        CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
        if (!displayEntry)
        {
            sLog->outErrorDb("Creature (Entry: %u) lists non-existing Modelid2 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid2);
            const_cast<CreatureInfo*>(cInfo)->Modelid2 = 0;
        }
        else if (!displayScaleEntry)
            displayScaleEntry = displayEntry;

        CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid2);
        if (!minfo)
            sLog->outErrorDb("No model data exist for `Modelid2` = %u listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry);
    }

    if (cInfo->Modelid3)
    {
        CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
        if (!displayEntry)
        {
            sLog->outErrorDb("Creature (Entry: %u) lists non-existing Modelid3 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid3);
            const_cast<CreatureInfo*>(cInfo)->Modelid3 = 0;
        }
        else if (!displayScaleEntry)
            displayScaleEntry = displayEntry;

        CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid3);
        if (!minfo)
            sLog->outErrorDb("No model data exist for `Modelid3` = %u listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry);
    }

    if (cInfo->Modelid4)
    {
        CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
        if (!displayEntry)
        {
            sLog->outErrorDb("Creature (Entry: %u) lists non-existing Modelid4 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid4);
            const_cast<CreatureInfo*>(cInfo)->Modelid4 = 0;
        }
        else if (!displayScaleEntry)
            displayScaleEntry = displayEntry;

        CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid4);
        if (!minfo)
            sLog->outErrorDb("No model data exist for `Modelid4` = %u listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry);
    }

    if (!displayScaleEntry)
        sLog->outErrorDb("Creature (Entry: %u) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry);

    for (int k = 0; k < MAX_KILL_CREDIT; ++k)
    {
        if (cInfo->KillCredit[k])
        {
            if (!GetCreatureTemplate(cInfo->KillCredit[k]))
            {
                sLog->outErrorDb("Creature (Entry: %u) lists non-existing creature entry %u in `KillCredit%d`.", cInfo->Entry, cInfo->KillCredit[k], k + 1);
                const_cast<CreatureInfo*>(cInfo)->KillCredit[k] = 0;
            }
        }
    }

    if (!cInfo->unit_class || ((1 << (cInfo->unit_class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
    {
        sLog->outErrorDb("Creature (Entry: %u) has invalid unit_class (%u) in creature_template. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
        const_cast<CreatureInfo*>(cInfo)->unit_class = UNIT_CLASS_WARRIOR;
    }

    if (cInfo->dmgschool >= MAX_SPELL_SCHOOL)
    {
        sLog->outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`.", cInfo->Entry, cInfo->dmgschool);
        const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
    }

    if (cInfo->baseattacktime == 0)
        const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;

    if (cInfo->rangeattacktime == 0)
        const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;

    if (cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK)
    {
        sLog->outErrorDb("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it is expected to be set by code handling `npc_spellclick_spells` content.", cInfo->Entry, UNIT_NPC_FLAG_SPELLCLICK);
        const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
    }

    if ((cInfo->npcflag & UNIT_NPC_FLAG_TRAINER) && cInfo->trainer_type >= MAX_TRAINER_TYPE)
        sLog->outErrorDb("Creature (Entry: %u) has wrong trainer type %u.", cInfo->Entry, cInfo->trainer_type);

    if (cInfo->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
    {
        sLog->outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`.", cInfo->Entry, cInfo->type);
        const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
    }

    // must exist or used hidden but used in data horse case
    if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM)
    {
        sLog->outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family);
        const_cast<CreatureInfo*>(cInfo)->family = 0;
    }

    if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
    {
        sLog->outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly.", cInfo->Entry, cInfo->InhabitType);
        const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
    }

    if (cInfo->VehicleId)
    {
        VehicleEntry const* vehId = sVehicleStore.LookupEntry(cInfo->VehicleId);
        if (!vehId)
        {
            sLog->outErrorDb("Creature (Entry: %u) has a non-existing VehicleId (%u). This *WILL* cause the client to freeze!", cInfo->Entry, cInfo->VehicleId);
            const_cast<CreatureInfo*>(cInfo)->VehicleId = 0;
        }
    }

    if (cInfo->PetSpellDataId)
    {
        CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
        if (!spellDataId)
            sLog->outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u).", cInfo->Entry, cInfo->PetSpellDataId);
    }

    for (uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j)
    {
        if (cInfo->spells[j] && !sSpellStore.LookupEntry(cInfo->spells[j]))
        {
            sLog->outErrorDb("Creature (Entry: %u) has non-existing Spell%d (%u), set to 0.", cInfo->Entry, j + 1, cInfo->spells[j]);
            const_cast<CreatureInfo*>(cInfo)->spells[j] = 0;
        }
    }

    if (cInfo->MovementType >= MAX_DB_MOTION_TYPE)
    {
        sLog->outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignored and set to IDLE.", cInfo->Entry, cInfo->MovementType);
        const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
    }

    if (cInfo->equipmentId > 0)          // 0 no equipment
    {
        if (!GetEquipmentInfo(cInfo->equipmentId))
        {
            sLog->outErrorDb("Table `creature_template` lists creature (Entry: %u) with `equipment_id` %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
            const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
        }
    }

    /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
    if (cInfo->scale <= 0.0f)
    {
        if (displayScaleEntry)
            const_cast<CreatureInfo*>(cInfo)->scale = displayScaleEntry->scale;
        else
            const_cast<CreatureInfo*>(cInfo)->scale = 1.0f;
    }

    if (cInfo->expansion > (MAX_CREATURE_BASE_HP - 1))
    {
        sLog->outErrorDb("Table `creature_template` lists creature (Entry: %u) with expansion %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
        const_cast<CreatureInfo*>(cInfo)->expansion = 0;
    }

    if (uint32 badFlags = (cInfo->flags_extra & ~CREATURE_FLAG_EXTRA_DB_ALLOWED))
    {
        sLog->outErrorDb("Table `creature_template` lists creature (Entry: %u) with disallowed `flags_extra` %u, removing incorrect flag.", cInfo->Entry, badFlags);
        const_cast<CreatureInfo*>(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
    }

    const_cast<CreatureInfo*>(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank);
}

void ObjectMgr::ConvertCreatureAddonAuras (CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
{
    // Now add the auras, format "spellid effectindex spellid effectindex..."
    char *p, *s;
    std::map<uint32, uint32> val;
    s = p = (char*) reinterpret_cast<char const*>(addon->auras);
    if (p)
    {
        uint32 currSpellId = 0;
        bool spell = true;
        while (p[0] != 0)
        {
            ++p;
            if (p[0] == ' ' || p[0] == 0)
            {
                if (spell)
                    currSpellId = atoi(s);
                else
                {
                    uint8 eff = atoi(s);
                    if (eff >= 3)
                    {
                        sLog->outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`(too high aura effect: %d for spell: %d)", guidEntryStr, addon->guidOrEntry, table, eff, currSpellId);
                    }
                    val[currSpellId] |= 1 << eff;
                }
                spell = !spell;
                if (p[0] == 0)
                    break;
                s = ++p;
            }
        }

        // free char* loaded memory
        delete[] (char*) reinterpret_cast<char const*>(addon->auras);

        // wrong list
        if (!spell)
        {
            addon->auras = NULL;
            sLog->outErrorDb("Creature (%s: %u) has wrong `auras` data in `%s`.", guidEntryStr, addon->guidOrEntry, table);
            return;
        }
    }

    // empty list
    if (val.empty())
    {
        addon->auras = NULL;
        return;
    }

    // replace by new structures array
    const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size() + 1];

    uint32 i = 0;
    for (std::map<uint32, uint32>::iterator itr = val.begin(); itr != val.end(); ++itr)
    {
        CreatureDataAddonAura& cAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
        cAura.spell_id = itr->first;
        cAura.effectMask = itr->second;
        if (cAura.effectMask > 7 || !cAura.effectMask)
        {
            sLog->outErrorDb("Creature (%s: %u) has wrong effect for spell %u in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, cAura.spell_id, table);
            continue;
        }
        SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(cAura.spell_id);
        if (!AdditionalSpellInfo)
        {
            sLog->outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, cAura.spell_id, table);
            continue;
        }
        for (uint8 eff = 0; eff < MAX_SPELL_EFFECTS; ++eff)
        {
            if ((1 << eff) & cAura.effectMask)
            {
                if (!AdditionalSpellInfo->Effect[eff] || !AdditionalSpellInfo->EffectApplyAuraName[eff])
                {
                    sLog->outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, eff, cAura.spell_id, table);
                    continue;
                }
                else if (AdditionalSpellInfo->Effect[eff] == SPELL_EFFECT_PERSISTENT_AREA_AURA)
                {
                    sLog->outErrorDb("Creature (%s: %u) has persistent area aura effect %u of spell %u defined in `auras` field in `%s`.", guidEntryStr, addon->guidOrEntry, eff, cAura.spell_id, table);
                    continue;
                }
            }
        }

        ++i;
    }

    // fill terminator element (after last added)
    CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
    endAura.spell_id = 0;
    endAura.effectMask = 0;
}

void ObjectMgr::LoadCreatureAddons (SQLStorage& creatureaddons, char const* entryName, char const* comment)
{
    creatureaddons.Load();

    sLog->outString(">> Loaded %u %s", creatureaddons.RecordCount, comment);
    sLog->outString();

    // check data correctness and convert 'auras'
    for (uint32 i = 1; i < creatureaddons.MaxEntry; ++i)
    {
        CreatureDataAddon const* addon = creatureaddons.LookupEntry<CreatureDataAddon>(i);
        if (!addon)
            continue;

        if (addon->mount)
        {
            if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount))
            {
                sLog->outErrorDb("Creature (%s %u) have invalid displayInfoId for mount (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->mount, creatureaddons.GetTableName());
                const_cast<CreatureDataAddon*>(addon)->mount = 0;
            }
        }

        if (!sEmotesStore.LookupEntry(addon->emote))
            sLog->outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->emote, creatureaddons.GetTableName());

        /*if (addon->move_flags & (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4))
         {
         sLog->outErrorDb("Creature (%s %u) movement flags mask defined in `%s` include forbidden  flags (" I32FMT ") that can crash client, cleanup at load.", entryName, addon->guidOrEntry, creatureaddons.GetTableName(), (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4));
         const_cast<CreatureDataAddon*>(addon)->move_flags &= ~(MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4);
         }*/

        ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), creatureaddons.GetTableName(), entryName);
    }
}

void ObjectMgr::LoadCreatureAddons ()
{
    LoadCreatureAddons(sCreatureInfoAddonStorage, "Entry", "creature template addons");

    // check entry ids
    for (uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
        if (CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i))
            if (!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
                sLog->outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`", addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName());

    sLog->outString("Loading Creature Addon Data...");
    LoadCreatureAddons(sCreatureDataAddonStorage, "GUID", "creature addons");

    // check entry ids
    for (uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
        if (CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i))
            if (mCreatureDataMap.find(addon->guidOrEntry) == mCreatureDataMap.end())
                sLog->outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`", addon->guidOrEntry);
}

EquipmentInfo const* ObjectMgr::GetEquipmentInfo (uint32 entry)
{
    return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
}

void ObjectMgr::LoadEquipmentTemplates ()
{
    sEquipmentStorage.Load();

    for (uint32 i = 0; i < sEquipmentStorage.MaxEntry; ++i)
    {
        EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i);

        if (!eqInfo)
            continue;

        for (uint8 j = 0; j < 3; ++j)
        {
            if (!eqInfo->equipentry[j])
                continue;

            ItemEntry const *db2item = sItemStore.LookupEntry(eqInfo->equipentry[j]);

            if (!db2item)
            {
                sLog->outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j + 1, i);
                const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
                continue;
            }

            if (db2item->InventoryType != INVTYPE_WEAPON && db2item->InventoryType != INVTYPE_SHIELD && db2item->InventoryType != INVTYPE_RANGED && db2item->InventoryType != INVTYPE_2HWEAPON && db2item->InventoryType != INVTYPE_WEAPONMAINHAND && db2item->InventoryType != INVTYPE_WEAPONOFFHAND && db2item->InventoryType != INVTYPE_HOLDABLE && db2item->InventoryType != INVTYPE_THROWN && db2item->InventoryType != INVTYPE_RANGEDRIGHT)
            {
                sLog->outErrorDb("Item (entry=%u) in creature_equip_template.equipentry%u for entry = %u is not equipable in a hand, forced to 0.", eqInfo->equipentry[j], j + 1, i);
                const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
            }
        }
    }

    sLog->outString(">> Loaded %u equipment template", sEquipmentStorage.RecordCount);
    sLog->outString();
}

CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo (uint32 modelid)
{
    return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
}

uint32 ObjectMgr::ChooseDisplayId (uint32 /*team*/, const CreatureInfo *cinfo, const CreatureData *data /*= NULL*/)
{
    // Load creature model (display id)
    uint32 display_id = 0;

    if (!data || data->displayid == 0)
    {
        display_id = cinfo->GetRandomValidModelId();
    }
    else
        return data->displayid;

    return display_id;
}

void ObjectMgr::ChooseCreatureFlags (const CreatureInfo *cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, const CreatureData *data /*= NULL*/)
{
    npcflag = cinfo->npcflag;
    unit_flags = cinfo->unit_flags;
    dynamicflags = cinfo->dynamicflags;

    if (data)
    {
        if (data->npcflag)
            npcflag = data->npcflag;

        if (data->unit_flags)
            unit_flags = data->unit_flags;

        if (data->dynamicflags)
            dynamicflags = data->dynamicflags;
    }
}

CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender (uint32 display_id)
{
    CreatureModelInfo const *minfo = GetCreatureModelInfo(display_id);
    if (!minfo)
        return NULL;

    // If a model for another gender exists, 50% chance to use it
    if (minfo->modelid_other_gender != 0 && urand(0, 1) == 0)
    {
        CreatureModelInfo const *minfo_tmp = GetCreatureModelInfo(minfo->modelid_other_gender);
        if (!minfo_tmp)
        {
            sLog->outErrorDb("Model (Entry: %u) has modelid_other_gender %u not found in table `creature_model_info`. ", minfo->modelid, minfo->modelid_other_gender);
            return minfo;          // not fatal, just use the previous one
        }
        else
            return minfo_tmp;
    }
    else
        return minfo;
}

void ObjectMgr::LoadCreatureModelInfo ()
{
    sCreatureModelStorage.Load();

    // post processing
    for (uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i)
    {
        CreatureModelInfo const *minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
        if (!minfo)
            continue;

        if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid))
            sLog->outErrorDb("Table `creature_model_info` has model for not existed display id (%u).", minfo->modelid);

        if (minfo->gender > GENDER_NONE)
        {
            sLog->outErrorDb("Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(minfo->gender), minfo->modelid);
            const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE;
        }

        if (minfo->modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender))
        {
            sLog->outErrorDb("Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", minfo->modelid_other_gender, minfo->modelid);
            const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
        }
    }

    sLog->outString(">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount);
    sLog->outString();

    // check if combat_reach is valid
    for (uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i)
    {
        CreatureModelInfo const* mInfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
        if (!mInfo)
            continue;

        if (mInfo->combat_reach < 0.1f)
        {
            //sLog->outErrorDb("Creature model (Entry: %u) has invalid combat reach (%f), setting it to 0.5", mInfo->modelid, mInfo->combat_reach);
            const_cast<CreatureModelInfo*>(mInfo)->combat_reach = DEFAULT_COMBAT_REACH;
        }
    }
}

void ObjectMgr::LoadLinkedRespawn ()
{
    uint32 oldMSTime = getMSTime();

    mLinkedRespawnMap.clear();
    QueryResult result = WorldDatabase.Query("SELECT guid, linkedGuid, linkType FROM linked_respawn ORDER BY guid ASC");

    if (!result)
    {
        sLog->outErrorDb(">> Loaded 0 linked respawns. DB table `linked_respawn` is empty.");
        sLog->outString();
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 guidLow = fields[0].GetUInt32();
        uint32 linkedGuidLow = fields[1].GetUInt32();
        uint8 linkType = fields[2].GetUInt8();

        uint64 guid, linkedGuid;
        bool error = false;
        switch (linkType)
        {
        case CREATURE_TO_CREATURE:
        {
            const CreatureData* slave = GetCreatureData(guidLow);
            if (!slave)
            {
                sLog->outErrorDb("Couldn't get creature data for GUIDLow %u", guidLow);
                error = true;
                break;
            }

            const CreatureData* master = GetCreatureData(linkedGuidLow);
            if (!master)
            {
                sLog->outErrorDb("Couldn't get creature data for GUIDLow %u", linkedGuidLow);
                error = true;
                break;
            }

            const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
            if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
            {
                sLog->outErrorDb("Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            if (!(master->spawnMask & slave->spawnMask))          // they must have a possibility to meet (normal/heroic difficulty)
            {
                sLog->outErrorDb("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_UNIT);
            linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_UNIT);
            break;
        }
        case CREATURE_TO_GO:
        {
            const CreatureData* slave = GetCreatureData(guidLow);
            if (!slave)
            {
                sLog->outErrorDb("Couldn't get creature data for GUIDLow %u", guidLow);
                error = true;
                break;
            }

            const GameObjectData* master = GetGOData(linkedGuidLow);
            if (!master)
            {
                sLog->outErrorDb("Couldn't get gameobject data for GUIDLow %u", linkedGuidLow);
                error = true;
                break;
            }

            const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
            if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
            {
                sLog->outErrorDb("Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            if (!(master->spawnMask & slave->spawnMask))          // they must have a possibility to meet (normal/heroic difficulty)
            {
                sLog->outErrorDb("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_UNIT);
            linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_GAMEOBJECT);
            break;
        }
        case GO_TO_GO:
        {
            const GameObjectData* slave = GetGOData(guidLow);
            if (!slave)
            {
                sLog->outErrorDb("Couldn't get gameobject data for GUIDLow %u", guidLow);
                error = true;
                break;
            }

            const GameObjectData* master = GetGOData(linkedGuidLow);
            if (!master)
            {
                sLog->outErrorDb("Couldn't get gameobject data for GUIDLow %u", linkedGuidLow);
                error = true;
                break;
            }

            const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
            if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
            {
                sLog->outErrorDb("Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            if (!(master->spawnMask & slave->spawnMask))          // they must have a possibility to meet (normal/heroic difficulty)
            {
                sLog->outErrorDb("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_GAMEOBJECT);
            linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_GAMEOBJECT);
            break;
        }
        case GO_TO_CREATURE:
        {
            const GameObjectData* slave = GetGOData(guidLow);
            if (!slave)
            {
                sLog->outErrorDb("Couldn't get gameobject data for GUIDLow %u", guidLow);
                error = true;
                break;
            }

            const CreatureData* master = GetCreatureData(linkedGuidLow);
            if (!master)
            {
                sLog->outErrorDb("Couldn't get creature data for GUIDLow %u", linkedGuidLow);
                error = true;
                break;
            }

            const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
            if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
            {
                sLog->outErrorDb("Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            if (!(master->spawnMask & slave->spawnMask))          // they must have a possibility to meet (normal/heroic difficulty)
            {
                sLog->outErrorDb("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
                error = true;
                break;
            }

            guid = MAKE_NEW_GUID(guidLow, slave->id, HIGHGUID_GAMEOBJECT);
            linkedGuid = MAKE_NEW_GUID(linkedGuidLow, master->id, HIGHGUID_UNIT);
            break;
        }
        }

        if (!error)
            mLinkedRespawnMap[guid] = linkedGuid;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded " UI64FMTD " linked respawns in %u ms", uint64(mLinkedRespawnMap.size()), GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

bool ObjectMgr::SetCreatureLinkedRespawn (uint32 guidLow, uint32 linkedGuidLow)
{
    if (!guidLow)
        return false;

    const CreatureData* master = GetCreatureData(guidLow);
    uint64 guid = MAKE_NEW_GUID(guidLow, master->id, HIGHGUID_UNIT);

    if (!linkedGuidLow)          // we're removing the linking
    {
        mLinkedRespawnMap.erase(guid);
        PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN);
        stmt->setUInt32(0, guidLow);
        WorldDatabase.Execute(stmt);
        return true;
    }

    const CreatureData* slave = GetCreatureData(linkedGuidLow);

    const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
    if (!map || !map->Instanceable() || (master->mapid != slave->mapid))
    {
        sLog->outErrorDb("Creature '%u' linking to '%u' on an unpermitted map.", guidLow, linkedGuidLow);
        return false;
    }

    if (!(master->spawnMask & slave->spawnMask))          // they must have a possibility to meet (normal/heroic difficulty)
    {
        sLog->outErrorDb("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask", guidLow, linkedGuidLow);
        return false;
    }

    uint64 linkedGuid = MAKE_NEW_GUID(linkedGuidLow, slave->id, HIGHGUID_UNIT);

    mLinkedRespawnMap[guid] = linkedGuid;
    PreparedStatement *stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CRELINKED_RESPAWN);
    stmt->setUInt32(0, guidLow);
    stmt->setUInt32(1, linkedGuidLow);
    WorldDatabase.Execute(stmt);
    return true;
}

void ObjectMgr::LoadCreatures ()
{
    uint32 count = 0;
    //                                                0              1   2    3
    QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid, "
    //   4             5           6           7           8            9              10         11
            "equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint, "
            //   12         13       14          15            16         17         18     19
            "curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event, pool_entry, "
            //   20                21                   22
            "creature.npcflag, creature.unit_flags, creature.dynamicflags "
            "FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
            "LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
        return;
    }

    // build single time for check creature data
    std::set<uint32> difficultyCreatures[MAX_DIFFICULTY - 1];
    for (uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
        if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
            for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1; ++diff)
                if (cInfo->DifficultyEntry[diff])
                    difficultyCreatures[diff].insert(cInfo->DifficultyEntry[diff]);

    // build single time for check spawnmask
    std::map<uint32, uint32> spawnMasks;
    for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
        if (sMapStore.LookupEntry(i))
            for (int k = 0; k < MAX_DIFFICULTY; ++k)
                if (GetMapDifficultyData(i, Difficulty(k)))
                    spawnMasks[i] |= (1 << k);

    //TODO: remove this
    //sGameEventMgr->mGameEventCreatureGuids.resize(52*2-1);

    do
    {
        Field *fields = result->Fetch();

        uint32 guid = fields[0].GetUInt32();
        uint32 entry = fields[1].GetUInt32();

        CreatureInfo const* cInfo = GetCreatureTemplate(entry);
        if (!cInfo)
        {
            sLog->outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
            continue;
        }

        CreatureData& data = mCreatureDataMap[guid];

        data.id = entry;
        data.mapid = fields[2].GetUInt32();
        data.displayid = fields[3].GetUInt32();
        data.equipmentId = fields[4].GetUInt32();
        data.posX = fields[5].GetFloat();
        data.posY = fields[6].GetFloat();
        data.posZ = fields[7].GetFloat();
        data.orientation = fields[8].GetFloat();
        data.spawntimesecs = fields[9].GetUInt32();
        data.spawndist = fields[10].GetFloat();
        data.currentwaypoint = fields[11].GetUInt32();
        data.curhealth = fields[12].GetUInt32();
        data.curmana = fields[13].GetUInt32();
        data.is_dead = fields[14].GetBool();
        data.movementType = fields[15].GetUInt8();
        data.spawnMask = fields[16].GetUInt8();
        data.phaseMask = fields[17].GetUInt16();
        int16 gameEvent = fields[18].GetInt16();
        uint32 PoolId = fields[19].GetUInt32();
        data.npcflag = fields[20].GetUInt32();
        data.unit_flags = fields[21].GetUInt32();
        data.dynamicflags = fields[22].GetUInt32();

        MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
        if (!mapEntry)
        {
            sLog->outErrorDb("Table `creature` have creature (GUID: %u) that spawned at not existed map (Id: %u), skipped.", guid, data.mapid);
            continue;
        }

        bool ok = true;
        for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1 && ok; ++diff)
        {
            if (difficultyCreatures[diff].find(data.id) != difficultyCreatures[diff].end())
            {
                sLog->outErrorDb("Table `creature` have creature (GUID: %u) that listed as difficulty %u template (entry: %u) in `creature_template`, skipped.", guid, diff + 1, data.id);
                ok = false;
            }
        }
        if (!ok)
            continue;

        // I do not know why but in db most display id are not zero
        /*if (data.displayid == 11686 || data.displayid == 24719)
         {
         (const_cast<CreatureInfo*>(cInfo))->flags_extra |= CREATURE_FLAG_EXTRA_TRIGGER;
         }
         else if (data.displayid == cInfo->DisplayID_A || data.displayid == cInfo->DisplayID_A2
         || data.displayid == cInfo->DisplayID_H || data.displayid == cInfo->DisplayID_H2)
         data.displayid = 0;
         */

        if (data.equipmentId > 0)          // -1 no equipment, 0 use default
        {
            if (!GetEquipmentInfo(data.equipmentId))
            {
                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);
                data.equipmentId = -1;
            }
        }

        if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
        {
            if (!mapEntry || !mapEntry->IsDungeon())
                sLog->outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.", guid, data.id);
        }

        if (data.spawndist < 0.0f)
        {
            sLog->outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.", guid, data.id);
            data.spawndist = 0.0f;
        }
        else if (data.movementType == RANDOM_MOTION_TYPE)
        {
            if (data.spawndist == 0.0f)
            {
                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);
                data.movementType = IDLE_MOTION_TYPE;
            }
            else if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER)
                data.movementType = IDLE_MOTION_TYPE;
        }
        else if (data.movementType == IDLE_MOTION_TYPE)
        {
            if (data.spawndist != 0.0f)
            {
                sLog->outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `MovementType`=0 (idle) have `spawndist`<>0, set to 0.", guid, data.id);
                data.spawndist = 0.0f;
            }
        }

        if (data.phaseMask == 0)
        {
            sLog->outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
            data.phaseMask = 1;
        }

        if (data.npcflag & UNIT_NPC_FLAG_SPELLCLICK)
        {
            sLog->outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with npcflag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it is expected to be set by code handling `npc_spellclick_spells` content.", guid, data.id, UNIT_NPC_FLAG_SPELLCLICK);
            data.npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
        }

        //if (entry == 32307 || entry == 32308)
        /*if (entry == 30739 || entry == 30740)
         {
         gameEvent = 51;
         uint32 guid2 = sObjectMgr->GenerateLowGuid(HIGHGUID_UNIT);
         CreatureData& data2 = mCreatureDataMap[guid2];
         data2 = data;
         //            data2.id = (entry == 32307 ? 32308 : 32307);
         data2.id = (entry == 30739 ? 30740 : 30739);
         data2.displayid = 0;
         sGameEventMgr->mGameEventCreatureGuids[51+51].push_back(guid);
         sGameEventMgr->mGameEventCreatureGuids[51+50].push_back(guid2);
         }*/

        if (gameEvent == 0 && PoolId == 0)          // if not this is to be managed by GameEvent System or Pool system
            AddCreatureToGrid(guid, &data);

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u creatures", (uint32) mCreatureDataMap.size());
}

void ObjectMgr::AddCreatureToGrid (uint32 guid, CreatureData const* data)
{
    uint8 mask = data->spawnMask;
    for (uint8 i = 0; mask != 0; i++, mask >>= 1)
    {
        if (mask & 1)
        {
            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
            uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;

            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
            cell_guids.creatures.insert(guid);
        }
    }
}

void ObjectMgr::RemoveCreatureFromGrid (uint32 guid, CreatureData const* data)
{
    uint8 mask = data->spawnMask;
    for (uint8 i = 0; mask != 0; i++, mask >>= 1)
    {
        if (mask & 1)
        {
            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
            uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;

            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
            cell_guids.creatures.erase(guid);
        }
    }
}

uint32 ObjectMgr::AddGOData (uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3)
{
    GameObjectInfo const* goinfo = GetGameObjectInfo(entry);
    if (!goinfo)
        return 0;

    Map* map = const_cast<Map*>(sMapMgr->CreateBaseMap(mapId));
    if (!map)
        return 0;

    uint32 guid = GenerateLowGuid(HIGHGUID_GAMEOBJECT);
    GameObjectData& data = NewGOData(guid);
    data.id = entry;
    data.mapid = mapId;
    data.posX = x;
    data.posY = y;
    data.posZ = z;
    data.orientation = o;
    data.rotation0 = rotation0;
    data.rotation1 = rotation1;
    data.rotation2 = rotation2;
    data.rotation3 = rotation3;
    data.spawntimesecs = spawntimedelay;
    data.animprogress = 100;
    data.spawnMask = 1;
    data.go_state = GO_STATE_READY;
    data.phaseMask = PHASEMASK_NORMAL;
    data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
    data.dbData = false;

    AddGameobjectToGrid(guid, &data);

    // Spawn if necessary (loaded grids only)
    // We use spawn coords to spawn
    if (!map->Instanceable() && map->IsLoaded(x, y))
    {
        GameObject *go = new GameObject;
        if (!go->LoadFromDB(guid, map))
        {
            sLog->outError("AddGOData: cannot add gameobject entry %u to map", entry);
            delete go;
            return 0;
        }
        map->Add(go);
    }

    sLog->outDebug(LOG_FILTER_MAPS, "AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, x, y, z, o);

    return guid;
}

bool ObjectMgr::MoveCreData (uint32 guid, uint32 mapId, Position pos)
{
    CreatureData& data = NewOrExistCreatureData(guid);
    if (!data.id)
        return false;

    RemoveCreatureFromGrid(guid, &data);
    if (data.posX == pos.GetPositionX() && data.posY == pos.GetPositionY() && data.posZ == pos.GetPositionZ())
        return true;
    data.posX = pos.GetPositionX();
    data.posY = pos.GetPositionY();
    data.posZ = pos.GetPositionZ();
    data.orientation = pos.GetOrientation();
    AddCreatureToGrid(guid, &data);

    // Spawn if necessary (loaded grids only)
    if (Map* map = const_cast<Map*>(sMapMgr->CreateBaseMap(mapId)))
    {
        // We use spawn coords to spawn
        if (!map->Instanceable() && map->IsLoaded(data.posX, data.posY))
        {
            CreatureInfo const *ci = ObjectMgr::GetCreatureTemplate(data.id);
            if (!ci)
                return 0;

            Creature* creature = NULL;
            if (ci->ScriptID)
                creature = sScriptMgr->GetCreatureScriptedClass(ci->ScriptID);
            if (creature == NULL)
                creature = new Creature();

            if (!creature->LoadFromDB(guid, map))
            {
                sLog->outError("AddCreature: cannot add creature entry %u to map", guid);
                delete creature;
                return false;
            }
            map->Add(creature);
        }
    }
    return true;
}

uint32 ObjectMgr::AddCreData (uint32 entry, uint32 /*team*/, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay)
{
    CreatureInfo const *cInfo = GetCreatureTemplate(entry);
    if (!cInfo)
        return 0;

    uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel);          // Only used for extracting creature base stats
    CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(level, cInfo->unit_class);

    uint32 guid = GenerateLowGuid(HIGHGUID_UNIT);
    CreatureData& data = NewOrExistCreatureData(guid);
    data.id = entry;
    data.mapid = mapId;
    data.displayid = 0;
    data.equipmentId = cInfo->equipmentId;
    data.posX = x;
    data.posY = y;
    data.posZ = z;
    data.orientation = o;
    data.spawntimesecs = spawntimedelay;
    data.spawndist = 0;
    data.currentwaypoint = 0;
    data.curhealth = stats->GenerateHealth(cInfo);
    data.curmana = stats->GenerateMana(cInfo);
    data.is_dead = false;
    data.movementType = cInfo->MovementType;
    data.spawnMask = 1;
    data.phaseMask = PHASEMASK_NORMAL;
    data.dbData = false;
    data.npcflag = cInfo->npcflag;
    data.unit_flags = cInfo->unit_flags;
    data.dynamicflags = cInfo->dynamicflags;

    AddCreatureToGrid(guid, &data);

    // Spawn if necessary (loaded grids only)
    if (Map* map = const_cast<Map*>(sMapMgr->CreateBaseMap(mapId)))
    {
        // We use spawn coords to spawn
        if (!map->Instanceable() && !map->IsRemovalGrid(x, y))
        {
            CreatureInfo const *ci = ObjectMgr::GetCreatureTemplate(entry);
            if (!ci)
                return 0;

            Creature* creature = NULL;
            if (ci->ScriptID)
                creature = sScriptMgr->GetCreatureScriptedClass(ci->ScriptID);
            if (creature == NULL)
                creature = new Creature();

            if (!creature->LoadFromDB(guid, map))
            {
                sLog->outError("AddCreature: cannot add creature entry %u to map", entry);
                delete creature;
                return 0;
            }
            map->Add(creature);
        }
    }

    return guid;
}

void ObjectMgr::LoadGameobjects ()
{
    uint32 count = 0;

    //                                                0                1   2    3           4           5           6
    QueryResult result = WorldDatabase.Query("SELECT gameobject.guid, id, map, position_x, position_y, position_z, orientation, "
    //   7          8          9          10         11             12            13     14         15         16     17
            "rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event, pool_entry "
            "FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
            "LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_gameobject.guid");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 gameobjects. DB table `gameobject` is empty.");
        return;
    }

    // build single time for check spawnmask
    std::map<uint32, uint32> spawnMasks;
    for (uint32 i = 0; i < sMapStore.GetNumRows(); ++i)
        if (sMapStore.LookupEntry(i))
            for (int k = 0; k < MAX_DIFFICULTY; ++k)
                if (GetMapDifficultyData(i, Difficulty(k)))
                    spawnMasks[i] |= (1 << k);

    do
    {
        Field *fields = result->Fetch();

        uint32 guid = fields[0].GetUInt32();
        uint32 entry = fields[1].GetUInt32();

        GameObjectInfo const* gInfo = GetGameObjectInfo(entry);
        if (!gInfo)
        {
            sLog->outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
            continue;
        }

        if (!gInfo->displayId)
        {
            switch (gInfo->type)
            {
            case GAMEOBJECT_TYPE_TRAP:
            case GAMEOBJECT_TYPE_SPELL_FOCUS:
                break;
            default:
                sLog->outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) doesn't have displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
                break;
            }
        }

        if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
        {
            sLog->outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.", guid, entry, gInfo->type, gInfo->displayId);
            continue;
        }

        GameObjectData& data = mGameObjectDataMap[guid];

        data.id = entry;
        data.mapid = fields[2].GetUInt32();
        data.posX = fields[3].GetFloat();
        data.posY = fields[4].GetFloat();
        data.posZ = fields[5].GetFloat();
        data.orientation = fields[6].GetFloat();
        data.rotation0 = fields[7].GetFloat();
        data.rotation1 = fields[8].GetFloat();
        data.rotation2 = fields[9].GetFloat();
        data.rotation3 = fields[10].GetFloat();
        data.spawntimesecs = fields[11].GetInt32();

        MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid);
        if (!mapEntry)
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) that spawned at not existed map (Id: %u), skip", guid, data.id, data.mapid);
            continue;
        }

        if (data.spawntimesecs == 0 && gInfo->IsDespawnAtAction())
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.", guid, data.id);
        }

        data.animprogress = fields[12].GetUInt32();
        data.artKit = 0;

        uint32 go_state = fields[13].GetUInt32();
        if (go_state >= MAX_GO_STATE)
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
            continue;
        }
        data.go_state = GOState(go_state);

        data.spawnMask = fields[14].GetUInt8();

        data.phaseMask = fields[15].GetUInt16();
        int16 gameEvent = fields[16].GetInt16();
        uint32 PoolId = fields[17].GetUInt32();

        if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2);
            continue;
        }

        if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3);
            continue;
        }

        if (!MapManager::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation))
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip", guid, data.id);
            continue;
        }

        if (data.phaseMask == 0)
        {
            sLog->outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.", guid, data.id);
            data.phaseMask = 1;
        }

        if (gameEvent == 0 && PoolId == 0)          // if not this is to be managed by GameEvent System or Pool system
            AddGameobjectToGrid(guid, &data);
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu gameobjects", (unsigned long) mGameObjectDataMap.size());
}

void ObjectMgr::AddGameobjectToGrid (uint32 guid, GameObjectData const* data)
{
    uint8 mask = data->spawnMask;
    for (uint8 i = 0; mask != 0; i++, mask >>= 1)
    {
        if (mask & 1)
        {
            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
            uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;

            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
            cell_guids.gameobjects.insert(guid);
        }
    }
}

void ObjectMgr::RemoveGameobjectFromGrid (uint32 guid, GameObjectData const* data)
{
    uint8 mask = data->spawnMask;
    for (uint8 i = 0; mask != 0; i++, mask >>= 1)
    {
        if (mask & 1)
        {
            CellPair cell_pair = Trinity::ComputeCellPair(data->posX, data->posY);
            uint32 cell_id = (cell_pair.y_coord * TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord;

            CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(data->mapid, i)][cell_id];
            cell_guids.gameobjects.erase(guid);
        }
    }
}

void ObjectMgr::LoadCreatureRespawnTimes ()
{
    uint32 count = 0;

    QueryResult result = CharacterDatabase.Query("SELECT guid, respawnTime, instanceId FROM creature_respawn");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 creature respawn time.");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 loguid = fields[0].GetUInt32();
        uint64 respawn_time = fields[1].GetUInt32();
        uint32 instance = fields[2].GetUInt32();

        mCreatureRespawnTimes[MAKE_PAIR64(loguid, instance)] = time_t(respawn_time);

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu creature respawn times", (unsigned long) mCreatureRespawnTimes.size());
}

void ObjectMgr::LoadGameobjectRespawnTimes ()
{
    // remove outdated data
    PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_GO_RESPAWNS);
    CharacterDatabase.DirectExecute(stmt);

    uint32 count = 0;

    QueryResult result = CharacterDatabase.Query("SELECT guid, respawnTime, instanceId FROM gameobject_respawn");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 gameobject respawn time.");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 loguid = fields[0].GetUInt32();
        uint32 respawn_time = fields[1].GetUInt32();
        uint32 instance = fields[2].GetUInt32();

        mGORespawnTimes[MAKE_PAIR64(loguid, instance)] = time_t(respawn_time);

        ++count;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded %lu gameobject respawn times", (unsigned long) mGORespawnTimes.size());
    sLog->outString();
}

Player* ObjectMgr::GetPlayerByLowGUID (uint32 lowguid) const
{
    uint64 guid = MAKE_NEW_GUID(lowguid, 0, HIGHGUID_PLAYER);
    return ObjectAccessor::FindPlayer(guid);
}

// name must be checked to correctness (if received) before call this function
uint64 ObjectMgr::GetPlayerGUIDByName (std::string name) const
{
    uint64 guid = 0;

    CharacterDatabase.EscapeString(name);

    // Player name safe to sending to DB (checked at login) and this function using
    QueryResult result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE name = '%s'", name.c_str());
    if (result)
        guid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);

    return guid;
}

bool ObjectMgr::GetPlayerNameByGUID (const uint64 &guid, std::string &name) const
{
    // prevent DB access for online player
    if (Player* player = GetPlayer(guid))
    {
        name = player->GetName();
        return true;
    }

    QueryResult result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid = '%u'", GUID_LOPART(guid));

    if (result)
    {
        name = (*result)[0].GetString();
        return true;
    }

    return false;
}

uint32 ObjectMgr::GetPlayerTeamByGUID (const uint64 &guid) const
{
    // prevent DB access for online player
    if (Player* player = GetPlayer(guid))
    {
        return Player::TeamForRace(player->getRace());
    }

    QueryResult result = CharacterDatabase.PQuery("SELECT race FROM characters WHERE guid = '%u'", GUID_LOPART(guid));

    if (result)
    {
        uint8 race = (*result)[0].GetUInt8();
        return Player::TeamForRace(race);
    }

    return 0;
}

uint32 ObjectMgr::GetPlayerAccountIdByGUID (const uint64 &guid) const
{
    // prevent DB access for online player
    if (Player* player = GetPlayer(guid))
    {
        return player->GetSession()->GetAccountId();
    }

    QueryResult result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
    if (result)
    {
        uint32 acc = (*result)[0].GetUInt32();
        return acc;
    }

    return 0;
}

uint32 ObjectMgr::GetPlayerAccountIdByPlayerName (const std::string& name) const
{
    QueryResult result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", name.c_str());
    if (result)
    {
        uint32 acc = (*result)[0].GetUInt32();
        return acc;
    }

    return 0;
}

void ObjectMgr::LoadItemLocales ()
{
    mItemLocaleMap.clear();          // need for reload case

    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");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        ItemLocale& data = mItemLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            LocaleConstant locale = (LocaleConstant) i;
            std::string str = fields[1 + 2 * (i - 1)].GetString();
            AddLocaleString(str, locale, data.Name);

            str = fields[1 + 2 * (i - 1) + 1].GetString();
            AddLocaleString(str, locale, data.Description);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu Item locale strings", (unsigned long) mItemLocaleMap.size());
}

struct SQLItemLoader: public SQLStorageLoaderBase<SQLItemLoader>
{
    template<class D>
    void convert_from_str (uint32 /*field_pos*/, char *src, D &dst)
    {
        dst = D(sObjectMgr->GetScriptId(src));
    }
};

void ObjectMgr::LoadItemPrototypes ()
{
    SQLItemLoader loader;
    loader.Load(sItemStorage);
    sLog->outString(">> Loaded %u item prototypes", sItemStorage.RecordCount);
    sLog->outString();

    // check data correctness
    for (uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
    {
        ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i);
        ItemEntry const* db2item = sItemStore.LookupEntry(i);

        if (!proto)
            continue;

        if (db2item)
        {
            if (proto->Class != db2item->Class)
            {
                sLog->outErrorDb("Item (Entry: %u) does not have a correct class %u, must be %u .", i, proto->Class, db2item->Class);
            }

            if (proto->SubClass != db2item->SubClass)
            {
                sLog->outErrorDb("Item (Entry: %u) not correct (Class: %u, Sub: %u) pair, must be (Class: %u, Sub: %u) (still using DB value).", i, proto->Class, proto->SubClass, db2item->Class, db2item->SubClass);
                // It safe let use Subclass from DB
            }

            if (proto->Unk0 != db2item->Unk0)
            {
                sLog->outErrorDb("Item (Entry: %u) does not have a correct Unk0 (%i) , must be %i (still using DB value).", i, proto->Unk0, db2item->Unk0);
                // It is safe to use Unk0 from DB
            }

            if (proto->Material != db2item->Material)
            {
                sLog->outErrorDb("Item (Entry: %u) does not have a correct material (%i), must be %i (still using DB value).", i, proto->Material, db2item->Material);
                // It is safe to use Material from DB
            }

            if (proto->InventoryType != db2item->InventoryType)
            {
                sLog->outErrorDb("Item (Entry: %u) does not have a correct inventory type (%u), must be %u (still using DB value).", i, proto->InventoryType, db2item->InventoryType);
                // It is safe to use InventoryType from DB
            }

            if (proto->DisplayInfoID != db2item->DisplayId)
            {
                sLog->outErrorDb("Item (Entry: %u) does not have a correct display id (%u), must be %u (using it).", i, proto->DisplayInfoID, db2item->DisplayId);
                const_cast<ItemPrototype*>(proto)->DisplayInfoID = db2item->DisplayId;
            }
            if (proto->Sheath != db2item->Sheath)
            {
                sLog->outErrorDb("Item (Entry: %u) does not have a correct sheathid (%u), must be %u  (using it).", i, proto->Sheath, db2item->Sheath);
                const_cast<ItemPrototype*>(proto)->Sheath = db2item->Sheath;
            }
        }
        else
            sLog->outErrorDb("Item (Entry: %u) does not exist in item.db2! (this client do not support this item entry).", i);

        if (proto->Class >= MAX_ITEM_CLASS)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong Class value (%u)", i, proto->Class);
            const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_MISC;
        }

        if (proto->SubClass >= MaxItemSubclassValues[proto->Class])
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u", i, proto->SubClass, proto->Class);
            const_cast<ItemPrototype*>(proto)->SubClass = 0;          // exist for all item classes
        }

        if (proto->Quality >= MAX_ITEM_QUALITY)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong Quality value (%u)", i, proto->Quality);
            const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
        }

        if (proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY)
        {
            if (FactionEntry const* faction = sFactionStore.LookupEntry(HORDE))
                if ((proto->AllowableRace & faction->BaseRepRaceMask[0]) == 0)
                    sLog->outErrorDb("Item (Entry: %u) have in `AllowableRace` races (%u) only not compatible with ITEM_FLAGS_EXTRA_HORDE_ONLY (%u) in Flags field, item any way will can't be equipped or use by this races.", i, proto->AllowableRace, ITEM_FLAGS_EXTRA_HORDE_ONLY);

            if (proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY)
                sLog->outErrorDb("Item (Entry: %u) have in `Flags2` flags ITEM_FLAGS_EXTRA_ALLIANCE_ONLY (%u) and ITEM_FLAGS_EXTRA_HORDE_ONLY (%u) in Flags field, this is wrong combination.", i, ITEM_FLAGS_EXTRA_ALLIANCE_ONLY, ITEM_FLAGS_EXTRA_HORDE_ONLY);
        }
        else if (proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY)
        {
            if (FactionEntry const* faction = sFactionStore.LookupEntry(ALLIANCE))
                if ((proto->AllowableRace & faction->BaseRepRaceMask[0]) == 0)
                    sLog->outErrorDb("Item (Entry: %u) have in `AllowableRace` races (%u) only not compatible with ITEM_FLAGS_EXTRA_ALLIANCE_ONLY (%u) in Flags field, item any way will can't be equipped or use by this races.", i, proto->AllowableRace, ITEM_FLAGS_EXTRA_ALLIANCE_ONLY);
        }

        if (proto->BuyCount <= 0)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).", i, proto->BuyCount);
            const_cast<ItemPrototype*>(proto)->BuyCount = 1;
        }

        if (proto->InventoryType >= MAX_INVTYPE)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)", i, proto->InventoryType);
            const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
        }

        if (proto->RequiredSkill >= MAX_SKILL_TYPE)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)", i, proto->RequiredSkill);
            const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
        }

        {
            // can be used in equip slot, as page read use in inventory, or spell casting at use
            bool req = proto->InventoryType != INVTYPE_NON_EQUIP || proto->PageText;
            if (!req)
                for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
                {
                    if (proto->Spells[j].SpellId)
                    {
                        req = true;
                        break;
                    }
                }

            if (req)
            {
                if (!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
                    sLog->outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.", i, proto->AllowableClass);

                if (!(proto->AllowableRace & RACEMASK_ALL_PLAYABLE))
                    sLog->outErrorDb("Item (Entry: %u) not have in `AllowableRace` any playable races (%u) and can't be equipped or use.", i, proto->AllowableRace);
            }
        }

        if (proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
        {
            sLog->outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)", i, proto->RequiredSpell);
            const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
        }

        if (proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
            sLog->outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.", i, proto->RequiredReputationRank);

        if (proto->RequiredReputationFaction)
        {
            if (!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
            {
                sLog->outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)", i, proto->RequiredReputationFaction);
                const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
            }

            if (proto->RequiredReputationRank == MIN_REPUTATION_RANK)
                sLog->outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.", i);
        }

        if (proto->MaxCount < -1)
        {
            sLog->outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.", i, proto->MaxCount);
            const_cast<ItemPrototype*>(proto)->MaxCount = -1;
        }

        if (proto->Stackable == 0)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.", i, proto->Stackable);
            const_cast<ItemPrototype*>(proto)->Stackable = 1;
        }
        else if (proto->Stackable < -1)
        {
            sLog->outErrorDb("Item (Entry: %u) has too large negative in stackable (%i), replace by value (-1) no stacking limits.", i, proto->Stackable);
            const_cast<ItemPrototype*>(proto)->Stackable = -1;
        }

        if (proto->ContainerSlots > MAX_BAG_SIZE)
        {
            sLog->outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).", i, proto->ContainerSlots, MAX_BAG_SIZE);
            const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE;
        }

        for (uint8 j = 0; j < MAX_ITEM_PROTO_STATS; ++j)
        {
            // for ItemStatValue != 0
            if (proto->ItemStat[j].ItemStatValue && proto->ItemStat[j].ItemStatType >= MAX_ITEM_MOD)
            {
                sLog->outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)", i, j + 1, proto->ItemStat[j].ItemStatType);
                const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
            }

            switch (proto->ItemStat[j].ItemStatType)
            {
            case ITEM_MOD_SPELL_HEALING_DONE:
            case ITEM_MOD_SPELL_DAMAGE_DONE:
                sLog->outErrorDb("Item (Entry: %u) has deprecated stat_type%d (%u)", i, j + 1, proto->ItemStat[j].ItemStatType);
                break;
            default:
                break;
            }
        }

        // special format
        if ((proto->Spells[0].SpellId == 483) || (proto->Spells[0].SpellId == 55884))
        {
            // spell_1
            if (proto->Spells[0].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
            {
                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);
                const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
                const_cast<ItemPrototype*>(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
                const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
                const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
            }

            // spell_2 have learning spell
            if (proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
            {
                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);
                const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
                const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
                const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
            }
            else if (!proto->Spells[1].SpellId)
            {
                sLog->outErrorDb("Item (Entry: %u) not has expected spell in spellid_%d in special learning format.", i, 1 + 1);
                const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
                const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
            }
            else if (proto->Spells[1].SpellId != -1)
            {
                SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[1].SpellId);
                if (!spellInfo && !sDisableMgr->IsDisabledFor(DISABLE_TYPE_SPELL, proto->Spells[1].SpellId, NULL))
                {
                    sLog->outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%d)", i, 1 + 1, proto->Spells[1].SpellId);
                    const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
                }
                // allowed only in special format
                else if ((proto->Spells[1].SpellId == 483) || (proto->Spells[1].SpellId == 55884))
                {
                    sLog->outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%d)", i, 1 + 1, proto->Spells[1].SpellId);
                    const_cast<ItemPrototype*>(proto)->Spells[0].SpellId = 0;
                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
                    const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
                }
            }

            // spell_3*, spell_4*, spell_5* is empty
            for (uint8 j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j)
            {
                if (proto->Spells[j].SpellTrigger != ITEM_SPELLTRIGGER_ON_USE)
                {
                    sLog->outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
                }
                else if (proto->Spells[j].SpellId != 0)
                {
                    sLog->outErrorDb("Item (Entry: %u) has wrong spell in spellid_%d (%d) for learning special format", i, j + 1, proto->Spells[j].SpellId);
                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
                }
            }
        }
        // normal spell list
        else
        {
            for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j)
            {
                if (proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
                {
                    sLog->outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger);
                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
                    const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
                }

                if (proto->Spells[j].SpellId && proto->Spells[j].SpellId != -1)
                {
                    SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
                    if (!spellInfo && !sDisableMgr->IsDisabledFor(DISABLE_TYPE_SPELL, proto->Spells[j].SpellId, NULL))
                    {
                        sLog->outErrorDb("Item (Entry: %u) has wrong (not existing) spell in spellid_%d (%d)", i, j + 1, proto->Spells[j].SpellId);
                        const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
                    }
                    // allowed only in special format
                    else if ((proto->Spells[j].SpellId == 483) || (proto->Spells[j].SpellId == 55884))
                    {
                        sLog->outErrorDb("Item (Entry: %u) has broken spell in spellid_%d (%d)", i, j + 1, proto->Spells[j].SpellId);
                        const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
                    }
                }
            }
        }

        if (proto->Bonding >= MAX_BIND_TYPE)
            sLog->outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)", i, proto->Bonding);

        if (proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
            sLog->outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i, proto->PageText);

        if (proto->LockID && !sLockStore.LookupEntry(proto->LockID))
            sLog->outErrorDb("Item (Entry: %u) has wrong LockID (%u)", i, proto->LockID);

        if (proto->Sheath >= MAX_SHEATHETYPE)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong Sheath (%u)", i, proto->Sheath);
            const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
        }

        if (proto->RandomProperty)
        {
            // To be implemented later
            if (proto->RandomProperty == -1)
                const_cast<ItemPrototype*>(proto)->RandomProperty = 0;

            else if (!sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
            {
                sLog->outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)", i, proto->RandomProperty);
                const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
            }
        }

        if (proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)", i, proto->RandomSuffix);
            const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
        }

        if (proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
        {
            sLog->outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)", i, proto->ItemSet);
            const_cast<ItemPrototype*>(proto)->ItemSet = 0;
        }

        if (proto->Area && !GetAreaEntryByAreaID(proto->Area))
            sLog->outErrorDb("Item (Entry: %u) has wrong Area (%u)", i, proto->Area);

        if (proto->Map && !sMapStore.LookupEntry(proto->Map))
            sLog->outErrorDb("Item (Entry: %u) has wrong Map (%u)", i, proto->Map);

        if (proto->BagFamily)
        {
            // check bits
            for (uint32 j = 0; j < sizeof(proto->BagFamily) * 8; ++j)
            {
                uint32 mask = 1 << j;
                if ((proto->BagFamily & mask) == 0)
                    continue;

                ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j + 1);
                if (!bf)
                {
                    sLog->outErrorDb("Item (Entry: %u) has bag family bit set not listed in ItemBagFamily.dbc, remove bit", i);
                    const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
                    continue;
                }
            }
        }

        if (proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
            sLog->outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)", i, proto->TotemCategory);

        for (uint8 j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
        {
            if (proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
            {
                sLog->outErrorDb("Item (Entry: %u) has wrong socketColor_%d (%u)", i, j + 1, proto->Socket[j].Color);
                const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
            }
        }

        if (proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
            sLog->outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)", i, proto->GemProperties);

        if (proto->RequiredDisenchantSkill < -1)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong RequiredDisenchantSkill (%i), set to (-1).", i, proto->RequiredDisenchantSkill);
            const_cast<ItemPrototype*>(proto)->RequiredDisenchantSkill = -1;
        }
        else if (proto->RequiredDisenchantSkill != -1)
        {
            if (proto->Quality > ITEM_QUALITY_EPIC || proto->Quality < ITEM_QUALITY_UNCOMMON)
            {
                sLog->outErrorDb("Item (Entry: %u) has unexpected RequiredDisenchantSkill (%u) for non-disenchantable quality (%u), reset it.", i, proto->RequiredDisenchantSkill, proto->Quality);
                const_cast<ItemPrototype*>(proto)->RequiredDisenchantSkill = -1;
            }
            else if (proto->Class != ITEM_CLASS_WEAPON && proto->Class != ITEM_CLASS_ARMOR)
            {
                // some wrong data in wdb for unused items
                sLog->outErrorDb("Item (Entry: %u) has unexpected RequiredDisenchantSkill (%u) for non-disenchantable item class (%u), reset it.", i, proto->RequiredDisenchantSkill, proto->Class);
                const_cast<ItemPrototype*>(proto)->RequiredDisenchantSkill = -1;
            }
        }

        if (proto->DisenchantID)
        {
            if (proto->Quality > ITEM_QUALITY_EPIC || proto->Quality < ITEM_QUALITY_UNCOMMON)
            {
                sLog->outErrorDb("Item (Entry: %u) has wrong quality (%u) for disenchanting, remove disenchanting loot id.", i, proto->Quality);
                const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
            }
            else if (proto->Class != ITEM_CLASS_WEAPON && proto->Class != ITEM_CLASS_ARMOR)
            {
                sLog->outErrorDb("Item (Entry: %u) has wrong item class (%u) for disenchanting, remove disenchanting loot id.", i, proto->Class);
                const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
            }
            else if (proto->RequiredDisenchantSkill < 0)
            {
                sLog->outErrorDb("Item (Entry: %u) marked as non-disenchantable by RequiredDisenchantSkill == -1, remove disenchanting loot id.", i);
                const_cast<ItemPrototype*>(proto)->DisenchantID = 0;
            }
        }
        else
        {
            // lot DB cases
            if (proto->RequiredDisenchantSkill >= 0)
                sLog->outErrorDb("Item (Entry: %u) marked as disenchantable by RequiredDisenchantSkill, but not have disenchanting loot id.", i);
        }

        if (proto->FoodType >= MAX_PET_DIET)
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)", i, proto->FoodType);
            const_cast<ItemPrototype*>(proto)->FoodType = 0;
        }

        if (proto->ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(proto->ItemLimitCategory))
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)", i, proto->ItemLimitCategory);
            const_cast<ItemPrototype*>(proto)->ItemLimitCategory = 0;
        }

        if (proto->HolidayId && !sHolidaysStore.LookupEntry(proto->HolidayId))
        {
            sLog->outErrorDb("Item (Entry: %u) has wrong HolidayId value (%u)", i, proto->HolidayId);
            const_cast<ItemPrototype*>(proto)->HolidayId = 0;
        }
    }

    // check some dbc referecned items (avoid duplicate reports)
    std::set<uint32> notFoundOutfit;
    for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
    {
        CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i);
        if (!entry)
            continue;

        for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
        {
            if (entry->ItemId[j] <= 0)
                continue;

            uint32 item_id = entry->ItemId[j];

            if (!ObjectMgr::GetItemPrototype(item_id))
                notFoundOutfit.insert(item_id);
        }
    }

    for (std::set<uint32>::const_iterator itr = notFoundOutfit.begin(); itr != notFoundOutfit.end(); ++itr)
        sLog->outErrorDb("Item (Entry: %u) not exist in `item_template` but referenced in `CharStartOutfit.dbc`", *itr);
}

void ObjectMgr::LoadItemSetNameLocales ()
{
    mItemSetNameLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT `entry`, `name_loc1`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc5`, `name_loc6`, `name_loc7`, `name_loc8` FROM `locales_item_set_names`");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        ItemSetNameLocale& data = mItemSetNameLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            std::string str = fields[i].GetString();
            AddLocaleString(str, LocaleConstant(i), data.Name);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded " UI64FMTD " Item set name locale strings", uint64(mItemSetNameLocaleMap.size()));
}

void ObjectMgr::LoadItemSetNames ()
{
    mItemSetNameMap.clear();          // needed for reload case

    QueryResult result = WorldDatabase.Query("SELECT `entry`, `name`, `InventoryType` FROM `item_set_names`");

    uint32 count = 0;
    std::set<uint32> itemSetItems;

    // fill item set member ids
    for (uint32 entryId = 0; entryId < sItemSetStore.GetNumRows(); ++entryId)
    {
        ItemSetEntry const* setEntry = sItemSetStore.LookupEntry(entryId);
        if (!setEntry)
            continue;

        for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i)
            if (setEntry->itemId[i])
                itemSetItems.insert(setEntry->itemId[i]);
    }

    if (result)
    {
        do
        {
            Field *fields = result->Fetch();

            uint32 entry = fields[0].GetUInt32();
            if (itemSetItems.find(entry) == itemSetItems.end())
            {
                sLog->outErrorDb("Item set name (Entry: %u) not found in ItemSet.dbc, data useless.", entry);
                continue;
            }

            ItemSetNameEntry &data = mItemSetNameMap[entry];
            data.name = fields[1].GetString();

            uint32 invType = fields[2].GetUInt32();
            if (invType >= MAX_INVTYPE)
            {
                sLog->outErrorDb("Item set name (Entry: %u) has wrong InventoryType value (%u)", entry, invType);
                invType = INVTYPE_NON_EQUIP;
            }

            data.InventoryType = invType;
            itemSetItems.erase(entry);
            ++count;
        }
        while (result->NextRow());
    }
    else
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 item set names. DB table `item_set_names` is empty.");
    }

    if (!itemSetItems.empty())
    {
        ItemPrototype const* pProto;
        for (std::set<uint32>::iterator itr = itemSetItems.begin(); itr != itemSetItems.end(); ++itr)
        {
            uint32 entry = *itr;
            // add data from item_template if available
            pProto = ObjectMgr::GetItemPrototype(entry);
            if (pProto)
            {
                sLog->outErrorDb("Item set part (Entry: %u) does not have entry in `item_set_names`, adding data from `item_template`.", entry);
                ItemSetNameEntry &data = mItemSetNameMap[entry];
                data.name = pProto->Name1;
                data.InventoryType = pProto->InventoryType;
                ++count;
            }
            else
                sLog->outErrorDb("Item set part (Entry: %u) does not have entry in `item_set_names`, set will not display properly.", entry);
        }
    }

    sLog->outString();
    sLog->outString(">> Loaded %u item set names", count);
}

void ObjectMgr::LoadVehicleAccessories ()
{
    m_VehicleAccessoryMap.clear();          // needed for reload case

    uint32 count = 0;

    QueryResult result = WorldDatabase.Query("SELECT `entry`, `accessory_entry`, `seat_id`, `minion` FROM `vehicle_accessory`");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 LoadVehicleAccessor. DB table `vehicle_accessory` is empty.");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 uiEntry = fields[0].GetUInt32();
        uint32 uiAccessory = fields[1].GetUInt32();
        int8 uiSeat = int8(fields[2].GetInt16());
        bool bMinion = fields[3].GetBool();

        if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiEntry))
        {
            sLog->outErrorDb("Table `vehicle_accessory`: creature template entry %u does not exist.", uiEntry);
            continue;
        }

        if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiAccessory))
        {
            sLog->outErrorDb("Table `vehicle_accessory`: Accessory %u does not exist.", uiAccessory);
            continue;
        }

        m_VehicleAccessoryMap[uiEntry].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion));

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u Vehicle Accessories", count);
}

void ObjectMgr::LoadVehicleScaling ()
{
    m_VehicleScalingMap.clear();          // needed for reload case

    uint32 count = 0;

    QueryResult result = WorldDatabase.Query("SELECT `entry`, `baseItemLevel`, `scalingFactor` FROM `vehicle_scaling_info`");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 vehicle scaling entries. DB table `vehicle_scaling_info` is empty.");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 vehicleEntry = fields[0].GetUInt32();
        float baseItemLevel = fields[1].GetFloat();
        float scalingFactor = fields[2].GetFloat();

        if (!sVehicleStore.LookupEntry(vehicleEntry))
        {
            sLog->outErrorDb("Table `vehicle_scaling_info`: vehicle entry %u does not exist.", vehicleEntry);
            continue;
        }

        m_VehicleScalingMap[vehicleEntry].ID = vehicleEntry;
        m_VehicleScalingMap[vehicleEntry].baseItemLevel = baseItemLevel;
        m_VehicleScalingMap[vehicleEntry].scalingFactor = scalingFactor;

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u vehicle scaling entries.", count);
}

void ObjectMgr::LoadPetLevelInfo ()
{
    // Loading levels data
    {
        //                                                 0               1      2   3     4    5    6    7     8    9
        QueryResult result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u level pet stats definitions", count);
            sLog->outErrorDb("Error loading `pet_levelstats` table or empty table.");
            return;
        }

        do
        {
            Field* fields = result->Fetch();

            uint32 creature_id = fields[0].GetUInt32();
            if (!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
            {
                sLog->outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.", creature_id);
                continue;
            }

            uint32 current_level = fields[1].GetUInt32();
            if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
            {
                if (current_level > STRONG_MAX_LEVEL)          // hardcoded level maximum
                    sLog->outErrorDb("Wrong (> %u) level %u in `pet_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
                else
                {
                    sLog->outDetail("Unused (> MaxPlayerLevel in worldserver.conf) level %u in `pet_levelstats` table, ignoring.", current_level);
                    ++count;          // make result loading percent "expected" correct in case disabled detail mode for example.
                }
                continue;
            }
            else if (current_level < 1)
            {
                sLog->outErrorDb("Wrong (<1) level %u in `pet_levelstats` table, ignoring.", current_level);
                continue;
            }

            PetLevelInfo*& pInfoMapEntry = petInfo[creature_id];

            if (pInfoMapEntry == NULL)
                pInfoMapEntry = new PetLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];

            // data for level 1 stored in [0] array element, ...
            PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];

            pLevelInfo->health = fields[2].GetUInt16();
            pLevelInfo->mana = fields[3].GetUInt16();
            pLevelInfo->armor = fields[9].GetUInt16();

            for (int i = 0; i < MAX_STATS; i++)
            {
                pLevelInfo->stats[i] = fields[i + 4].GetUInt16();
            }

            ++count;
        }
        while (result->NextRow());

        sLog->outString();
        sLog->outString(">> Loaded %u level pet stats definitions", count);
    }

    // Fill gaps and check integrity
    for (PetLevelInfoMap::iterator itr = petInfo.begin(); itr != petInfo.end(); ++itr)
    {
        PetLevelInfo* pInfo = itr->second;

        // fatal error if no level 1 data
        if (!pInfo || pInfo[0].health == 0)
        {
            sLog->outErrorDb("Creature %u does not have pet stats data for Level 1!", itr->first);
            exit(1);
        }

        // fill level gaps
        for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
        {
            if (pInfo[level].health == 0)
            {
                sLog->outErrorDb("Creature %u has no data for Level %i pet stats data, using data of Level %i.", itr->first, level + 1, level);
                pInfo[level] = pInfo[level - 1];
            }
        }
    }
}

PetLevelInfo const* ObjectMgr::GetPetLevelInfo (uint32 creature_id, uint8 level) const
{
    if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
        level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);

    PetLevelInfoMap::const_iterator itr = petInfo.find(creature_id);
    if (itr == petInfo.end())
        return NULL;

    return &itr->second[level - 1];          // data for level 1 stored in [0] array element, ...
}

void ObjectMgr::PlayerCreateInfoAddItemHelper (uint32 race_, uint32 class_, uint32 itemId, int32 count)
{
    if (count > 0)
        playerInfo[race_][class_].item.push_back(PlayerCreateInfoItem(itemId, count));
    else
    {
        if (count < -1)
            sLog->outErrorDb("Invalid count %i specified on item %u be removed from original player create info (use -1)!", count, itemId);

        uint32 RaceClass = (race_) | (class_ << 8);
        bool doneOne = false;
        for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
        {
            if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(i))
            {
                if (entry->RaceClassGender == RaceClass || entry->RaceClassGender == (RaceClass | (1 << 16)))
                {
                    bool found = false;
                    for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x)
                    {
                        if (entry->ItemId[x] > 0 && uint32(entry->ItemId[x]) == itemId)
                        {
                            found = true;
                            const_cast<CharStartOutfitEntry*>(entry)->ItemId[x] = 0;
                            break;
                        }
                    }

                    if (!found)
                        sLog->outErrorDb("Item %u specified to be removed from original create info not found in dbc!", itemId);

                    if (!doneOne)
                        doneOne = true;
                    else
                        break;
                }
            }
        }
    }
}

void ObjectMgr::LoadPlayerInfo ()
{
    // Load playercreate
    {
        //                                                0     1      2    3     4           5           6
        QueryResult result = WorldDatabase.Query("SELECT race, class, map, zone, position_x, position_y, position_z, orientation FROM playercreateinfo");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u player create definitions", count);
            sLog->outErrorDb("Error loading `playercreateinfo` table or empty table.");
            exit(1);
        }

        do
        {
            Field* fields = result->Fetch();

            uint32 current_race = fields[0].GetUInt32();
            uint32 current_class = fields[1].GetUInt32();
            uint32 mapId = fields[2].GetUInt32();
            uint32 areaId = fields[3].GetUInt32();
            float positionX = fields[4].GetFloat();
            float positionY = fields[5].GetFloat();
            float positionZ = fields[6].GetFloat();
            float orientation = fields[7].GetFloat();

            if (current_race >= MAX_RACES)
            {
                sLog->outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
                continue;
            }

            ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(current_race);
            if (!rEntry)
            {
                sLog->outErrorDb("Wrong race %u in `playercreateinfo` table, ignoring.", current_race);
                continue;
            }

            if (current_class >= MAX_CLASSES)
            {
                sLog->outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
                continue;
            }

            if (!sChrClassesStore.LookupEntry(current_class))
            {
                sLog->outErrorDb("Wrong class %u in `playercreateinfo` table, ignoring.", current_class);
                continue;
            }

            // accept DB data only for valid position (and non instanceable)
            if (!MapManager::IsValidMapCoord(mapId, positionX, positionY, positionZ, orientation))
            {
                sLog->outErrorDb("Wrong home position for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
                continue;
            }

            if (sMapStore.LookupEntry(mapId)->Instanceable())
            {
                sLog->outErrorDb("Home position in instanceable map for class %u race %u pair in `playercreateinfo` table, ignoring.", current_class, current_race);
                continue;
            }

            PlayerInfo* pInfo = &playerInfo[current_race][current_class];

            pInfo->mapId = mapId;
            pInfo->areaId = areaId;
            pInfo->positionX = positionX;
            pInfo->positionY = positionY;
            pInfo->positionZ = positionZ;
            pInfo->orientation = orientation;

            pInfo->displayId_m = rEntry->model_m;
            pInfo->displayId_f = rEntry->model_f;

            ++count;
        }
        while (result->NextRow());

        sLog->outString();
        sLog->outString(">> Loaded %u player create definitions", count);
    }

    // Load playercreate items
    sLog->outString("Loading Player Create Items Data...");
    {
        //                                                0     1      2       3
        QueryResult result = WorldDatabase.Query("SELECT race, class, itemid, amount FROM playercreateinfo_item");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u custom player create items", count);
        }
        else
        {
            do
            {
                Field* fields = result->Fetch();

                uint32 current_race = fields[0].GetUInt32();
                if (current_race >= MAX_RACES)
                {
                    sLog->outErrorDb("Wrong race %u in `playercreateinfo_item` table, ignoring.", current_race);
                    continue;
                }

                uint32 current_class = fields[1].GetUInt32();
                if (current_class >= MAX_CLASSES)
                {
                    sLog->outErrorDb("Wrong class %u in `playercreateinfo_item` table, ignoring.", current_class);
                    continue;
                }

                uint32 item_id = fields[2].GetUInt32();

                if (!ObjectMgr::GetItemPrototype(item_id))
                {
                    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);
                    continue;
                }

                int32 amount = fields[3].GetInt32();

                if (!amount)
                {
                    sLog->outErrorDb("Item id %u (class %u race %u) have amount == 0 in `playercreateinfo_item` table, ignoring.", item_id, current_race, current_class);
                    continue;
                }

                if (!current_race || !current_class)
                {
                    uint32 min_race = current_race ? current_race : 1;
                    uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
                    uint32 min_class = current_class ? current_class : 1;
                    uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
                    for (uint32 r = min_race; r < max_race; ++r)
                        for (uint32 c = min_class; c < max_class; ++c)
                            PlayerCreateInfoAddItemHelper(r, c, item_id, amount);
                }
                else
                    PlayerCreateInfoAddItemHelper(current_race, current_class, item_id, amount);

                ++count;
            }
            while (result->NextRow());

            sLog->outString();
            sLog->outString(">> Loaded %u custom player create items", count);
        }
    }

    // Load playercreate spells
    sLog->outString("Loading Player Create Spell Data...");
    {
        QueryResult result = QueryResult(NULL);
        if (sWorld->getBoolConfig(CONFIG_START_ALL_SPELLS))
            result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom");
        else
            result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u player create spells", count);
            sLog->outErrorDb("Error loading player starting spells or empty table.");
        }
        else
        {
            do
            {
                Field* fields = result->Fetch();

                uint32 current_race = fields[0].GetUInt32();
                if (current_race >= MAX_RACES)
                {
                    sLog->outErrorDb("Wrong race %u in `playercreateinfo_spell` table, ignoring.", current_race);
                    continue;
                }

                uint32 current_class = fields[1].GetUInt32();
                if (current_class >= MAX_CLASSES)
                {
                    sLog->outErrorDb("Wrong class %u in `playercreateinfo_spell` table, ignoring.", current_class);
                    continue;
                }

                if (!current_race || !current_class)
                {
                    uint32 min_race = current_race ? current_race : 1;
                    uint32 max_race = current_race ? current_race + 1 : MAX_RACES;
                    uint32 min_class = current_class ? current_class : 1;
                    uint32 max_class = current_class ? current_class + 1 : MAX_CLASSES;
                    for (uint32 r = min_race; r < max_race; ++r)
                        for (uint32 c = min_class; c < max_class; ++c)
                            playerInfo[r][c].spell.push_back(fields[2].GetUInt32());
                }
                else
                    playerInfo[current_race][current_class].spell.push_back(fields[2].GetUInt32());

                ++count;
            }
            while (result->NextRow());

            sLog->outString();
            sLog->outString(">> Loaded %u player create spells", count);
        }
    }

    // Load playercreate actions
    sLog->outString("Loading Player Create Action Data...");
    {
        //                                                0     1      2       3       4
        QueryResult result = WorldDatabase.Query("SELECT race, class, button, action, type FROM playercreateinfo_action");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u player create actions", count);
            sLog->outErrorDb("Error loading `playercreateinfo_action` table or empty table.");
        }
        else
        {
            do
            {
                Field* fields = result->Fetch();

                uint32 current_race = fields[0].GetUInt32();
                if (current_race >= MAX_RACES)
                {
                    sLog->outErrorDb("Wrong race %u in `playercreateinfo_action` table, ignoring.", current_race);
                    continue;
                }

                uint32 current_class = fields[1].GetUInt32();
                if (current_class >= MAX_CLASSES)
                {
                    sLog->outErrorDb("Wrong class %u in `playercreateinfo_action` table, ignoring.", current_class);
                    continue;
                }

                PlayerInfo* pInfo = &playerInfo[current_race][current_class];
                pInfo->action.push_back(PlayerCreateInfoAction(fields[2].GetUInt8(), fields[3].GetUInt32(), fields[4].GetUInt8()));

                ++count;
            }
            while (result->NextRow());

            sLog->outString();
            sLog->outString(">> Loaded %u player create actions", count);
        }
    }

    // Loading levels data (class only dependent)
    sLog->outString("Loading Player Create Level HP/Mana Data...");
    {
        //                                                 0      1      2       3
        QueryResult result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u level health/mana definitions", count);
            sLog->outErrorDb("Error loading `player_classlevelstats` table or empty table.");
            exit(1);
        }

        do
        {
            Field* fields = result->Fetch();

            uint32 current_class = fields[0].GetUInt32();
            if (current_class >= MAX_CLASSES)
            {
                sLog->outErrorDb("Wrong class %u in `player_classlevelstats` table, ignoring.", current_class);
                continue;
            }

            uint8 current_level = fields[1].GetUInt8();          // Can't be > than STRONG_MAX_LEVEL (hardcoded level maximum) due to var type
            if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
            {
                sLog->outDetail("Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_classlevelstats` table, ignoring.", current_level);
                ++count;          // make result loading percent "expected" correct in case disabled detail mode for example.
                continue;
            }

            PlayerClassInfo* pClassInfo = &playerClassInfo[current_class];

            if (!pClassInfo->levelInfo)
                pClassInfo->levelInfo = new PlayerClassLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];

            PlayerClassLevelInfo* pClassLevelInfo = &pClassInfo->levelInfo[current_level - 1];

            pClassLevelInfo->basehealth = fields[2].GetUInt16();
            pClassLevelInfo->basemana = fields[3].GetUInt16();

            ++count;
        }
        while (result->NextRow());

        sLog->outString();
        sLog->outString(">> Loaded %u level health/mana definitions", count);
    }

    // Fill gaps and check integrity
    for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
    {
        // skip non existed classes
        if (!sChrClassesStore.LookupEntry(class_))
            continue;

        PlayerClassInfo* pClassInfo = &playerClassInfo[class_];

        // fatal error if no level 1 data
        if (!pClassInfo->levelInfo || pClassInfo->levelInfo[0].basehealth == 0)
        {
            sLog->outErrorDb("Class %i Level 1 does not have health/mana data!", class_);
            exit(1);
        }

        // fill level gaps
        for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
        {
            if (pClassInfo->levelInfo[level].basehealth == 0)
            {
                sLog->outErrorDb("Class %i Level %i does not have health/mana data. Using stats data of level %i.", class_, level + 1, level);
                pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
            }
        }
    }

    // Loading levels data (class/race dependent)
    sLog->outString("Loading Player Create Level Stats Data...");
    {
        //                                                 0     1      2      3    4    5    6    7
        QueryResult result = WorldDatabase.Query("SELECT race, class, level, str, agi, sta, inte, spi FROM player_levelstats");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u level stats definitions", count);
            sLog->outErrorDb("Error loading `player_levelstats` table or empty table.");
            exit(1);
        }

        do
        {
            Field* fields = result->Fetch();

            uint32 current_race = fields[0].GetUInt32();
            if (current_race >= MAX_RACES)
            {
                sLog->outErrorDb("Wrong race %u in `player_levelstats` table, ignoring.", current_race);
                continue;
            }

            uint32 current_class = fields[1].GetUInt32();
            if (current_class >= MAX_CLASSES)
            {
                sLog->outErrorDb("Wrong class %u in `player_levelstats` table, ignoring.", current_class);
                continue;
            }

            uint32 current_level = fields[2].GetUInt32();
            if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
            {
                if (current_level > STRONG_MAX_LEVEL)          // hardcoded level maximum
                    sLog->outErrorDb("Wrong (> %u) level %u in `player_levelstats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
                else
                {
                    sLog->outDetail("Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_levelstats` table, ignoring.", current_level);
                    ++count;          // make result loading percent "expected" correct in case disabled detail mode for example.
                }
                continue;
            }

            PlayerInfo* pInfo = &playerInfo[current_race][current_class];

            if (!pInfo->levelInfo)
                pInfo->levelInfo = new PlayerLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];

            PlayerLevelInfo* pLevelInfo = &pInfo->levelInfo[current_level - 1];

            for (int i = 0; i < MAX_STATS; i++)
            {
                pLevelInfo->stats[i] = fields[i + 3].GetUInt8();
            }

            ++count;
        }
        while (result->NextRow());

        sLog->outString();
        sLog->outString(">> Loaded %u level stats definitions", count);
    }

    // Fill gaps and check integrity
    for (int race = 0; race < MAX_RACES; ++race)
    {
        // skip non existed races
        if (!sChrRacesStore.LookupEntry(race))
            continue;

        for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
        {
            // skip non existed classes
            if (!sChrClassesStore.LookupEntry(class_))
                continue;

            PlayerInfo* pInfo = &playerInfo[race][class_];

            // skip non loaded combinations
            if (!pInfo->displayId_m || !pInfo->displayId_f)
                continue;

            // skip expansion races if not playing with expansion
            if (sWorld->getIntConfig(CONFIG_EXPANSION) < 1 && (race == RACE_BLOODELF || race == RACE_DRAENEI))
                continue;

            // skip expansion classes if not playing with expansion
            if (sWorld->getIntConfig(CONFIG_EXPANSION) < 2 && class_ == CLASS_DEATH_KNIGHT)
                continue;

            if (sWorld->getIntConfig(CONFIG_EXPANSION) < 3 && (race == RACE_GOBLIN || race == RACE_WORGEN))
                continue;

            // fatal error if no level 1 data
            if (!pInfo->levelInfo || pInfo->levelInfo[0].stats[0] == 0)
            {
                sLog->outErrorDb("Race %i Class %i Level 1 does not have stats data!", race, class_);
                exit(1);
            }

            // fill level gaps
            for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
            {
                if (pInfo->levelInfo[level].stats[0] == 0)
                {
                    sLog->outErrorDb("Race %i Class %i Level %i does not have stats data. Using stats data of level %i.", race, class_, level + 1, level);
                    pInfo->levelInfo[level] = pInfo->levelInfo[level - 1];
                }
            }
        }
    }

    // Loading xp per level data
    sLog->outString("Loading Player Create XP Data...");
    {
        mPlayerXPperLevel.resize(sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL));
        for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
            mPlayerXPperLevel[level] = 0;

        //                                                 0    1
        QueryResult result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u xp for level definitions", count);
            sLog->outErrorDb("Error loading `player_xp_for_level` table or empty table.");
            exit(1);
        }

        do
        {
            Field* fields = result->Fetch();

            uint32 current_level = fields[0].GetUInt32();
            uint32 current_xp = fields[1].GetUInt32();

            if (current_level >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
            {
                if (current_level > STRONG_MAX_LEVEL)          // hardcoded level maximum
                    sLog->outErrorDb("Wrong (> %u) level %u in `player_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, current_level);
                else
                {
                    sLog->outDetail("Unused (> MaxPlayerLevel in worldserver.conf) level %u in `player_xp_for_levels` table, ignoring.", current_level);
                    ++count;          // make result loading percent "expected" correct in case disabled detail mode for example.
                }
                continue;
            }
            //PlayerXPperLevel
            mPlayerXPperLevel[current_level] = current_xp;

            ++count;
        }
        while (result->NextRow());

        sLog->outString();
        sLog->outString(">> Loaded %u xp for level definitions", count);
    }

    sLog->outString("Loading Guild XP Data...");
    {
        mGuildXPperLevel.resize(sWorld->getIntConfig(CONFIG_GUILD_ADVANCEMENT_MAX_LEVEL));
        for (uint8 level = 0; level < sWorld->getIntConfig(CONFIG_GUILD_ADVANCEMENT_MAX_LEVEL); ++level)
            mGuildXPperLevel[level] = 0;

        //                                                 0    1
        QueryResult result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM guild_xp_for_level");

        uint32 count = 0;

        if (!result)
        {
            sLog->outString();
            sLog->outString(">> Loaded %u xp for guild level definitions", count);
            sLog->outErrorDb("Error loading `guild_xp_for_level` table or empty table.");
            //exit(1);
        }
        else
        {
            do
            {
                Field* fields = result->Fetch();

                uint32 level = fields[0].GetUInt32();
                uint32 xp = fields[1].GetUInt32();

                if (level >= sWorld->getIntConfig(CONFIG_GUILD_ADVANCEMENT_MAX_LEVEL))
                {
                    if (level > STRONG_MAX_LEVEL)          // hardcoded level maximum
                        sLog->outErrorDb("Wrong (> %u) level %u in `guild_xp_for_level` table, ignoring.", STRONG_MAX_LEVEL, level);
                    else
                    {
                        sLog->outDetail("Unused (> MaxLevel in worldserver.conf) level %u in `guild_xp_for_levels` table, ignoring.", level);
                        ++count;          // make result loading percent "expected" correct in case disabled detail mode for example.
                    }
                    continue;
                }

                //PlayerXPperLevel
                mGuildXPperLevel[level] = xp;

                ++count;
            }
            while (result->NextRow());

            sLog->outString();
            sLog->outString(">> Loaded %u xp for guild level definitions", count);
        }
    }

    // fill level gaps
    for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
    {
        if (mPlayerXPperLevel[level] == 0)
        {
            sLog->outErrorDb("Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
            mPlayerXPperLevel[level] = mPlayerXPperLevel[level - 1] + 100;
        }
    }
    for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_GUILD_ADVANCEMENT_MAX_LEVEL); ++level)
    {
        if (mGuildXPperLevel[level] == 0)
        {
            sLog->outErrorDb("GUILD Level %i does not have XP for level data. Using data of level [%i] + 100.", level + 1, level);
            mGuildXPperLevel[level] = mGuildXPperLevel[level - 1] + 100;
        }
    }
}

void ObjectMgr::GetPlayerClassLevelInfo (uint32 class_, uint8 level, PlayerClassLevelInfo* info) const
{
    if (level < 1 || class_ >= MAX_CLASSES)
        return;

    PlayerClassInfo const* pInfo = &playerClassInfo[class_];

    if (level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
        level = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);

    *info = pInfo->levelInfo[level - 1];
}

void ObjectMgr::GetPlayerLevelInfo (uint32 race, uint32 class_, uint8 level, PlayerLevelInfo* info) const
{
    if (level < 1 || race >= MAX_RACES || class_ >= MAX_CLASSES)
        return;

    PlayerInfo const* pInfo = &playerInfo[race][class_];
    if (pInfo->displayId_m == 0 || pInfo->displayId_f == 0)
        return;

    if (level <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
        *info = pInfo->levelInfo[level - 1];
    else
        BuildPlayerLevelInfo(race, class_, level, info);
}

void ObjectMgr::BuildPlayerLevelInfo (uint8 race, uint8 _class, uint8 level, PlayerLevelInfo* info) const
{
    // base data (last known level)
    *info = playerInfo[race][_class].levelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1];

    // if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
    for (uint8 lvl = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) - 1; lvl < level; ++lvl)
    {
        switch (_class)
        {
        case CLASS_WARRIOR:
            info->stats[STAT_STRENGTH] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_STAMINA] += (lvl > 23 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_AGILITY] += (lvl > 36 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
            info->stats[STAT_INTELLECT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_SPIRIT] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            break;
        case CLASS_PALADIN:
            info->stats[STAT_STRENGTH] += (lvl > 3 ? 1 : 0);
            info->stats[STAT_STAMINA] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 7 && !(lvl % 2) ? 1 : 0));
            info->stats[STAT_INTELLECT] += (lvl > 6 && (lvl % 2) ? 1 : 0);
            info->stats[STAT_SPIRIT] += (lvl > 7 ? 1 : 0);
            break;
        case CLASS_HUNTER:
            info->stats[STAT_STRENGTH] += (lvl > 4 ? 1 : 0);
            info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
            info->stats[STAT_AGILITY] += (lvl > 33 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_INTELLECT] += (lvl > 8 && (lvl % 2) ? 1 : 0);
            info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
            break;
        case CLASS_ROGUE:
            info->stats[STAT_STRENGTH] += (lvl > 5 ? 1 : 0);
            info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
            info->stats[STAT_AGILITY] += (lvl > 16 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_INTELLECT] += (lvl > 8 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_SPIRIT] += (lvl > 38 ? 1 : (lvl > 9 && !(lvl % 2) ? 1 : 0));
            break;
        case CLASS_PRIEST:
            info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
            info->stats[STAT_AGILITY] += (lvl > 38 ? 1 : (lvl > 8 && (lvl % 2) ? 1 : 0));
            info->stats[STAT_INTELLECT] += (lvl > 22 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_SPIRIT] += (lvl > 3 ? 1 : 0);
            break;
        case CLASS_SHAMAN:
            info->stats[STAT_STRENGTH] += (lvl > 34 ? 1 : (lvl > 6 && (lvl % 2) ? 1 : 0));
            info->stats[STAT_STAMINA] += (lvl > 4 ? 1 : 0);
            info->stats[STAT_AGILITY] += (lvl > 7 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_INTELLECT] += (lvl > 5 ? 1 : 0);
            info->stats[STAT_SPIRIT] += (lvl > 4 ? 1 : 0);
            break;
        case CLASS_MAGE:
            info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_STAMINA] += (lvl > 5 ? 1 : 0);
            info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_INTELLECT] += (lvl > 24 ? 2 : (lvl > 1 ? 1 : 0));
            info->stats[STAT_SPIRIT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
            break;
        case CLASS_WARLOCK:
            info->stats[STAT_STRENGTH] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_STAMINA] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
            info->stats[STAT_AGILITY] += (lvl > 9 && !(lvl % 2) ? 1 : 0);
            info->stats[STAT_INTELLECT] += (lvl > 33 ? 2 : (lvl > 2 ? 1 : 0));
            info->stats[STAT_SPIRIT] += (lvl > 38 ? 2 : (lvl > 3 ? 1 : 0));
            break;
        case CLASS_DRUID:
            info->stats[STAT_STRENGTH] += (lvl > 38 ? 2 : (lvl > 6 && (lvl % 2) ? 1 : 0));
            info->stats[STAT_STAMINA] += (lvl > 32 ? 2 : (lvl > 4 ? 1 : 0));
            info->stats[STAT_AGILITY] += (lvl > 38 ? 2 : (lvl > 8 && (lvl % 2) ? 1 : 0));
            info->stats[STAT_INTELLECT] += (lvl > 38 ? 3 : (lvl > 4 ? 1 : 0));
            info->stats[STAT_SPIRIT] += (lvl > 38 ? 3 : (lvl > 5 ? 1 : 0));
            break;
        }
    }
}

void ObjectMgr::LoadGuilds ()
{
    PreparedStatement* stmt = NULL;
    PreparedQueryResult result;

    sLog->outString("Loading Guilds...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILDS);
    result = CharacterDatabase.Query(stmt);
    if (!result)
    {
        sLog->outString(">> Loaded 0 guild definitions");
        sLog->outString();
        return;
    }
    mGuildMap.resize(m_guildId, NULL);          // Reserve space and initialize storage for loading guilds
    // 1. Load all guilds
    uint64 rowCount = result->GetRowCount();
    do
    {
        Field* fields = result->Fetch();
        Guild* pNewGuild = new Guild();
        if (!pNewGuild->LoadFromDB(fields))
        {
            delete pNewGuild;
            continue;
        }
        AddGuild(pNewGuild);
    }
    while (result->NextRow());
    sLog->outString();
    sLog->outString(">> Loaded " UI64FMTD " guilds definitions", rowCount);
    sLog->outString();

    // 2. Load all guild ranks
    sLog->outString("Loading guild ranks...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_RANKS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[0].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadRankFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Loaded " UI64FMTD " ranks for all the guilds", rowCount);
    sLog->outString();

    // 3. Load all guild members
    sLog->outString("Loading guild members...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_MEMBERS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[0].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadMemberFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Loaded " UI64FMTD " members from all the guilds", rowCount);
    sLog->outString();

    // 4. Load all guild bank tab rights
    sLog->outString("Loading bank tab rights...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_BANK_RIGHTS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[0].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadBankRightFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Loaded " UI64FMTD " bank tab rights for all the guilds", rowCount);
    sLog->outString();

    // 5. Load all event logs
    sLog->outString("Loading guild event logs...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_EVENTLOGS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[0].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadEventLogFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Loaded " UI64FMTD " event logs for all the guilds", rowCount);
    sLog->outString();

    // 6. Load all bank event logs
    sLog->outString("Loading guild bank event logs...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_BANK_EVENTLOGS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[0].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadBankEventLogFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Loaded " UI64FMTD " bank event logs for all the guilds", rowCount);
    sLog->outString();

    // 7. Load all guild bank tabs
    sLog->outString("Loading guild bank tabs...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_BANK_TABS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[0].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadBankTabFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Loaded " UI64FMTD " bank tabs for all the guilds", rowCount);
    sLog->outString();

    // 8. Fill all guild bank tabs
    sLog->outString("Filling bank tabs with items...");
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_LOAD_GUILD_BANK_ITEMS);
    result = CharacterDatabase.Query(stmt);
    if (result)
    {
        rowCount = result->GetRowCount();
        do
        {
            Field* fields = result->Fetch();
            uint32 guildId = fields[11].GetUInt32();
            if (Guild* pGuild = GetGuildById(guildId))
                pGuild->LoadBankItemFromDB(fields);
        }
        while (result->NextRow());
    }
    else
    {
        rowCount = 0;
    }
    sLog->outString(">> Filled bank tabs with " UI64FMTD " items for all the guilds", rowCount);
    sLog->outString();

    // 9. Validate loaded guild data
    uint32 totalGuilds = 0;
    std::set<Guild*> rm;          // temporary storage to avoid modifying GuildStore with RemoveGuild() while iterating
    sLog->outString("Validating data of loaded guilds...");
    for (GuildMap::iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
    {
        Guild* pGuild = *itr;
        if (pGuild)
        {
            if (!pGuild->Validate())
                rm.insert(pGuild);
            else
                ++totalGuilds;
        }
    }
    for (std::set<Guild*>::iterator itr = rm.begin(); itr != rm.end(); ++itr)
    {
        Guild* pGuild = *itr;
        RemoveGuild(pGuild->GetId());
        delete pGuild;
    }
    // Cleanup
    // Delete orphan guild ranks
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_CLEAN_GUILD_RANKS);
    CharacterDatabase.Execute(stmt);
    // Delete orphan guild members
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_CLEAN_GUILD_MEMBERS);
    CharacterDatabase.Execute(stmt);
    // Delete orphan guild bank rights
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_CLEAN_GUILD_BANK_RIGHTS);
    CharacterDatabase.Execute(stmt);
    // Delete orphan guild bank tabs
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_CLEAN_GUILD_BANK_TABS);
    CharacterDatabase.Execute(stmt);
    // Delete orphan guild bank items
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_CLEAN_GUILD_BANK_ITEMS);
    CharacterDatabase.Execute(stmt);

    // Delete unused LogGuid records in guild_eventlog and guild_bank_eventlog table.
    // You can comment these lines if you don't plan to change CONFIG_GUILD_EVENT_LOG_COUNT and CONFIG_GUILD_BANK_EVENT_LOG_COUNT
    stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_GUILD_EVENT_LOGS);
    stmt->setUInt32(0, sWorld->getIntConfig(CONFIG_GUILD_EVENT_LOG_COUNT));
    CharacterDatabase.Execute(stmt);

    stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_GUILD_BANK_EVENT_LOGS);
    stmt->setUInt32(0, sWorld->getIntConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT));
    CharacterDatabase.Execute(stmt);

    sLog->outString();
    sLog->outString(">> Successfully loaded %u guilds", totalGuilds);
}

void ObjectMgr::LoadGuildRewards ()
{
    QueryResult result = WorldDatabase.Query("SELECT item_entry, price, achievement, standing FROM guild_rewards");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 guild reward definitions");
    }

    uint32 count = 0;
    do
    {
        Field *fields = result->Fetch();

        GuildRewardsEntry* ptr = new GuildRewardsEntry;
        ptr->item = fields[0].GetUInt32();
        ptr->price = fields[1].GetUInt32();
        ptr->achievement = fields[2].GetUInt32();
        ptr->standing = fields[3].GetUInt32();
        mGuildRewards.push_back(ptr);

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u guild reward definitions.");
}

void ObjectMgr::LoadArenaTeams ()
{
    uint32 oldMSTime = getMSTime();

    // Clean out the trash before loading anything
    CharacterDatabase.Execute("DELETE FROM arena_team_member WHERE arenaTeamId NOT IN (SELECT arenaTeamId FROM arena_team)");

    //                                                                   0        1         2         3          4              5            6            7           8
    QueryResult result = CharacterDatabase.Query("SELECT arena_team.arenaTeamId, name, captainGuid, type, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor, "
    //                                               9        10        11         12           13       14
            "rating, weekGames, weekWins, seasonGames, seasonWins, rank FROM arena_team ORDER BY arena_team.arenaTeamId ASC");

    if (!result)
    {
        sLog->outString(">> Loaded 0 arena teams. DB table `arena_team` is empty!");
        sLog->outString();
        return;
    }

    QueryResult result2 = CharacterDatabase.Query(
    //              0              1           2             3              4                 5          6     7          8                  9
    "SELECT arenaTeamId, atm.guid, atm.weekGames, atm.weekWins, atm.seasonGames, atm.seasonWins, c.name, class, atm.personalRating, matchMakerRating FROM arena_team_member atm"
            " INNER JOIN arena_team ate USING (arenaTeamId)"
            " LEFT JOIN characters AS c ON atm.guid = c.guid"
            " LEFT JOIN character_arena_stats AS cas ON c.guid = cas.guid AND (cas.slot = 0 AND ate.type = 2 OR cas.slot = 1 AND ate.type = 3 OR cas.slot = 2 AND ate.type = 5)"
            " ORDER BY atm.arenateamid ASC");

    uint32 count = 0;
    do
    {
        ArenaTeam* newArenaTeam = new ArenaTeam;

        if (!newArenaTeam->LoadArenaTeamFromDB(result) || !newArenaTeam->LoadMembersFromDB(result2))
        {
            newArenaTeam->Disband(NULL);
            delete newArenaTeam;
            continue;
        }

        AddArenaTeam(newArenaTeam);

        ++count;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded %u arena teams in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

void ObjectMgr::LoadGroups ()
{
    Group *group = NULL;
    Field *fields = NULL;
    uint32 count = 0;

    // Consistency cleaning before load to avoid having to do some checks later
    // Delete all members that does not exist
    CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_CHARACTER_GROUP_MEMBERS));
    // Delete all groups whose leader does not exist
    CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_DEL_LEADERLESS_GROUPS));
    // Delete all groups with less than 2 members
    CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_DEL_TINY_GROUPS));
    // Delete all rows from group_member or group_instance with no group
    CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GROUP_MEMBERS));
    CharacterDatabase.Execute(CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GROUP_INSTANCES));

    // ----------------------- Load Group definitions
    //                                                            0           1           2           3              4      5      6      7      8      9      10     11     12         13          14              15
    QueryResult result = CharacterDatabase.PQuery("SELECT leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raiddifficulty, guid FROM groups");
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 group definitions");
        return;
    }

    do
    {
        fields = result->Fetch();
        ++count;
        group = new Group;
        group->LoadGroupFromDB(fields[15].GetUInt32(), result, false);
        // group load will never be false (we have run consistency sql's before loading)
        AddGroup(group);
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u group definitions", count);

    // ----------------------- Load member
    //                                       0     1           2            3         4
    result = CharacterDatabase.Query("SELECT guid, memberGuid, memberFlags, subgroup, roles FROM group_member ORDER BY guid");
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 group members");
        return;
    }

    uint32 groupLowGuid = 0;
    count = 0;
    do
    {
        fields = result->Fetch();

        if (groupLowGuid != fields[0].GetUInt32())
        {
            groupLowGuid = fields[0].GetUInt32();
            group = GetGroupByGUID(groupLowGuid);
        }
        if (group)          // Should never be null
            group->LoadMemberFromDB(fields[1].GetUInt32(), fields[2].GetUInt8(), fields[3].GetUInt8(), fields[4].GetUInt8());
        else
            sLog->outError("ObjectMgr::LoadGroups: Consistency failed, can't find group (lowguid %u)", groupLowGuid);
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u group members", count);

    // ----------------------- Load instance save
    //                                       0     1    2         3          4           5
    result = CharacterDatabase.Query("SELECT guid, map, instance, permanent, difficulty, resettime, "
    //           6
            "(SELECT COUNT(1) FROM groups JOIN character_instance ON leaderGuid = groups.guid WHERE instance = group_instance.instance AND permanent = 1 LIMIT 1) "
            "FROM group_instance LEFT JOIN instance ON instance = id ORDER BY guid");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 group-instance saves");
        return;
    }

    count = 0;
    do
    {
        fields = result->Fetch();
        group = GetGroupByGUID(fields[0].GetUInt32());
        // group will never be NULL (we have run consistency sql's before loading)

        MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt32());
        if (!mapEntry || !mapEntry->IsDungeon())
        {
            sLog->outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt32());
            continue;
        }

        uint32 diff = fields[4].GetUInt8();
        if (diff >= uint32(mapEntry->IsRaid() ? MAX_RAID_DIFFICULTY : MAX_DUNGEON_DIFFICULTY))
        {
            sLog->outErrorDb("Wrong dungeon difficulty use in group_instance table: %d", diff + 1);
            diff = 0;          // default for both difficaly types
        }

        InstanceSave *save = sInstanceSaveMgr->AddInstanceSave(mapEntry->MapID, fields[2].GetUInt32(), Difficulty(diff), time_t(fields[5].GetUInt64()), fields[6].GetBool(), true);
        group->BindToInstance(save, fields[3].GetBool(), true);
        ++count;
    }
    while (result->NextRow());
    sLog->outString();
    sLog->outString(">> Loaded %u group-instance saves", count);
}

void ObjectMgr::LoadQuests ()
{
    // For reload case
    for (QuestMap::const_iterator itr = mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
        delete itr->second;
    mQuestTemplates.clear();

    mExclusiveQuestGroups.clear();

    //                                                       0      1       2           3                 4         5         6           7     8              9
    QueryResult result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClassMask, MinLevel, MaxLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue, "
    //   10                   11                 12                    13                  14                     15                   16                     17                   18                19
            "RepObjectiveFaction, RepObjectiveValue, RepObjectiveFaction2, RepObjectiveValue2, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime, "
            //   20          21            22           23            24            25                  26           27          28              29                30       31         32            33
            "QuestFlags, SpecialFlags, CharTitleId, PlayersSlain, BonusTalents, RewardArenaPoints, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, RewXPId, SrcItemId, SrcItemCount, SrcSpell, "
            //   34     35       36          37               38                39       40              41              42              43              44
            "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, CompletedText,  ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, "
            //   45          46          47          48          49          50          51             52             53             54             55             56
            "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemId5, ReqItemId6, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4, ReqItemCount5, ReqItemCount6, "
            //   57            58            59            60            61               62               63               64
            "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, "
            //   65                  66                  67                  68                  69                     70                     71                     72
            "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4, "
            //   73             74             75             76
            "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4, "
            //   77                78                79                80                81                82
            "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6, "
            //   83                   84                   85                   86                   87                   88
            "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6, "
            //   89          90          91          92          93             94             95             96
            "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4, "
            //   97              98              99              100             101             102             103             104             105             106
            "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValueId1, RewRepValueId2, RewRepValueId3, RewRepValueId4, RewRepValueId5, "
            //   107           108           109           110           111
            "RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5, "
            //   112               113                 114            115               116       117              118                119               120              121      122     123      124
            "RewHonorAddition, RewHonorMultiplier, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewSpellHiddenCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt, "
            //   125            126            127            128                 129                 130                 131               132
            "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, DetailsEmoteDelay1, DetailsEmoteDelay2, DetailsEmoteDelay3, DetailsEmoteDelay4, "
            //   133            134                135                136                137                138
            "IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4, "
            //   139                     140                     141                     142
            "OfferRewardEmoteDelay1, OfferRewardEmoteDelay2, OfferRewardEmoteDelay3, OfferRewardEmoteDelay4, "
            //   143             144         145                 146                 147
            "RewSkillLineId, RewSkillPoints, RewRepMask, QuestGiverPortrait, QuestTurnInPortrait, "
            //   148                149             150                151             152                153             154             155
            "RewCurrencyId1, RewCurrencyCount1, RewCurrencyId2, RewCurrencyCount2, RewCurrencyId3, RewCurrencyCount3, RewCurrencyId4, RewCurrencyCount4, "
            //   156                157             158                159             160                161             162             163
            "ReqCurrencyId1, ReqCurrencyCount1, ReqCurrencyId2, ReqCurrencyCount2, ReqCurrencyId3, ReqCurrencyCount3, ReqCurrencyId4, ReqCurrencyCount4, "
            //   164                    165                      166                     167              168             169                    170
            "QuestGiverPortraitText, QuestGiverPortraitUnk, QuestTurnInPortraitText, QuestTurnInPortraitUnk, QuestTargetMark, QuestStartType, SoundAccept, "
            //  171            172           173          174
            "SoundTurnIn, RequiredSpell, StartScript, CompleteScript"
            " FROM quest_template");
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 quests definitions");
        sLog->outErrorDb("`quest_template` table is empty!");
        return;
    }

    // create multimap previous quest for each existed quest
    // some quests can have many previous maps set by NextQuestId in previous quest
    // for example set of race quests can lead to single not race specific quest

    do
    {
        Field *fields = result->Fetch();

        Quest *newQuest = new Quest(fields);
        mQuestTemplates[newQuest->GetQuestId()] = newQuest;
    }
    while (result->NextRow());

    std::map<uint32, uint32> usedMailTemplates;

    // Post processing
    for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
    {
        // skip post-loading checks for disabled quests
        if (sDisableMgr->IsDisabledFor(DISABLE_TYPE_QUEST, iter->first, NULL))
            continue;

        Quest * qinfo = iter->second;

        // additional quest integrity checks (GO, creature_template and item_template must be loaded already)

        if (qinfo->GetQuestMethod() >= 3)
            sLog->outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());

        if (qinfo->SpecialFlags > QUEST_SPECIAL_FLAG_DB_ALLOWED)
        {
            sLog->outErrorDb("Quest %u has `SpecialFlags` = %u, above max flags not allowed for database.", qinfo->GetQuestId(), qinfo->SpecialFlags);
        }

        if (qinfo->HasFlag(QUEST_FLAGS_DAILY) && qinfo->HasFlag(QUEST_FLAGS_WEEKLY))
        {
            sLog->outErrorDb("Weekly Quest %u is marked as daily quest in `QuestFlags`, removed daily flag.", qinfo->GetQuestId());
            qinfo->QuestFlags &= ~QUEST_FLAGS_DAILY;
        }

        if (qinfo->HasFlag(QUEST_FLAGS_DAILY))
        {
            if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE))
            {
                sLog->outErrorDb("Daily Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
                qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE);
            }
        }

        if (qinfo->HasFlag(QUEST_FLAGS_WEEKLY))
        {
            if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE))
            {
                sLog->outErrorDb("Weekly Quest %u not marked as repeatable in `SpecialFlags`, added.", qinfo->GetQuestId());
                qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_REPEATABLE);
            }
        }

        if (qinfo->HasFlag(QUEST_FLAGS_AUTO_REWARDED))
        {
            // at auto-reward can be rewarded only RewChoiceItemId[0]
            for (int j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
            {
                if (uint32 id = qinfo->RewChoiceItemId[j])
                {
                    sLog->outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item from `RewChoiceItemId%d` can't be rewarded with quest flag QUEST_FLAGS_AUTO_REWARDED.", qinfo->GetQuestId(), j + 1, id, j + 1);
                    // no changes, quest ignore this data
                }
            }
        }

        // client quest log visual (area case)
        if (qinfo->ZoneOrSort > 0)
        {
            if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort))
            {
                sLog->outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.", qinfo->GetQuestId(), qinfo->ZoneOrSort);
                // no changes, quest not dependent from this value but can have problems at client
            }
        }
        // client quest log visual (sort case)
        if (qinfo->ZoneOrSort < 0)
        {
            QuestSortEntry const* qSort = sQuestSortStore.LookupEntry(-int32(qinfo->ZoneOrSort));
            if (!qSort)
            {
                sLog->outErrorDb("Quest %u has `ZoneOrSort` = %i (sort case) but quest sort with this id does not exist.", qinfo->GetQuestId(), qinfo->ZoneOrSort);
                // 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)
            }

            //check SkillOrClass value (class case).
            if (ClassByQuestSort(-int32(qinfo->ZoneOrSort)))
            {
                // SkillOrClass should not have class case when class case already set in ZoneOrSort.
                if (qinfo->SkillOrClassMask < 0)
                {
                    sLog->outErrorDb("Quest %u has `ZoneOrSort` = %i (class sort case) and `SkillOrClassMask` = %i (class case), redundant.", qinfo->GetQuestId(), qinfo->ZoneOrSort, qinfo->SkillOrClassMask);
                }
            }

            //check for proper SkillOrClass value (skill case)
            if (int32 skill_id = SkillByQuestSort(-int32(qinfo->ZoneOrSort)))
            {
                // skill is positive value in SkillOrClass
                if (qinfo->SkillOrClassMask != skill_id)
                {
                    sLog->outErrorDb("Quest %u has `ZoneOrSort` = %i (skill sort case) but `SkillOrClassMask` does not have a corresponding value (%i).", qinfo->GetQuestId(), qinfo->ZoneOrSort, skill_id);
                    //override, and force proper value here?
                }
            }
        }

        // SkillOrClassMask (class case)
        if (qinfo->SkillOrClassMask < 0)
        {
            if (!(-int32(qinfo->SkillOrClassMask) & CLASSMASK_ALL_PLAYABLE))
            {
                sLog->outErrorDb("Quest %u has `SkillOrClassMask` = %i (class case) but classmask does not have valid class", qinfo->GetQuestId(), qinfo->SkillOrClassMask);
            }
        }
        // SkillOrClassMask (skill case)
        else if (qinfo->SkillOrClassMask > 0)
        {
            if (!sSkillLineStore.LookupEntry(qinfo->SkillOrClassMask))
            {
                sLog->outErrorDb("Quest %u has `SkillOrClass` = %u (skill case) but skill (%i) does not exist", qinfo->GetQuestId(), qinfo->SkillOrClassMask, qinfo->SkillOrClassMask);
            }
        }

        if (qinfo->RequiredSkillValue)
        {
            if (qinfo->RequiredSkillValue > sWorld->GetConfigMaxSkillValue())
            {
                sLog->outErrorDb("Quest %u has `RequiredSkillValue` = %u but max possible skill is %u, quest can't be done.", qinfo->GetQuestId(), qinfo->RequiredSkillValue, sWorld->GetConfigMaxSkillValue());
                // no changes, quest can't be done for this requirement
            }

            if (qinfo->SkillOrClassMask <= 0)
            {
                sLog->outErrorDb("Quest %u has `RequiredSkillValue` = %u but `SkillOrClass` = %i (class case), value ignored.", qinfo->GetQuestId(), qinfo->RequiredSkillValue, qinfo->SkillOrClassMask);
                // no changes, quest can't be done for this requirement (fail at wrong skill id)
            }
        }
        // else Skill quests can have 0 skill level, this is ok

        if (qinfo->RepObjectiveFaction2 && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction2))
        {
            sLog->outErrorDb("Quest %u has `RepObjectiveFaction2` = %u but faction template %u does not exist, quest can't be done.", qinfo->GetQuestId(), qinfo->RepObjectiveFaction2, qinfo->RepObjectiveFaction2);
            // no changes, quest can't be done for this requirement
        }

        if (qinfo->RepObjectiveFaction && !sFactionStore.LookupEntry(qinfo->RepObjectiveFaction))
        {
            sLog->outErrorDb("Quest %u has `RepObjectiveFaction` = %u but faction template %u does not exist, quest can't be done.", qinfo->GetQuestId(), qinfo->RepObjectiveFaction, qinfo->RepObjectiveFaction);
            // no changes, quest can't be done for this requirement
        }

        if (qinfo->RequiredMinRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMinRepFaction))
        {
            sLog->outErrorDb("Quest %u has `RequiredMinRepFaction` = %u but faction template %u does not exist, quest can't be done.", qinfo->GetQuestId(), qinfo->RequiredMinRepFaction, qinfo->RequiredMinRepFaction);
            // no changes, quest can't be done for this requirement
        }

        if (qinfo->RequiredMaxRepFaction && !sFactionStore.LookupEntry(qinfo->RequiredMaxRepFaction))
        {
            sLog->outErrorDb("Quest %u has `RequiredMaxRepFaction` = %u but faction template %u does not exist, quest can't be done.", qinfo->GetQuestId(), qinfo->RequiredMaxRepFaction, qinfo->RequiredMaxRepFaction);
            // no changes, quest can't be done for this requirement
        }

        if (qinfo->RequiredMinRepValue && qinfo->RequiredMinRepValue > ReputationMgr::Reputation_Cap)
        {
            sLog->outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.", qinfo->GetQuestId(), qinfo->RequiredMinRepValue, ReputationMgr::Reputation_Cap);
            // no changes, quest can't be done for this requirement
        }

        if (qinfo->RequiredMinRepValue && qinfo->RequiredMaxRepValue && qinfo->RequiredMaxRepValue <= qinfo->RequiredMinRepValue)
        {
            sLog->outErrorDb("Quest %u has `RequiredMaxRepValue` = %d and `RequiredMinRepValue` = %d, quest can't be done.", qinfo->GetQuestId(), qinfo->RequiredMaxRepValue, qinfo->RequiredMinRepValue);
            // no changes, quest can't be done for this requirement
        }

        if (!qinfo->RepObjectiveFaction && qinfo->RepObjectiveValue > 0)
        {
            sLog->outErrorDb("Quest %u has `RepObjectiveValue` = %d but `RepObjectiveFaction` is 0, value has no effect", qinfo->GetQuestId(), qinfo->RepObjectiveValue);
            // warning
        }

        if (!qinfo->RepObjectiveFaction2 && qinfo->RepObjectiveValue2 > 0)
        {
            sLog->outErrorDb("Quest %u has `RepObjectiveValue2` = %d but `RepObjectiveFaction2` is 0, value has no effect", qinfo->GetQuestId(), qinfo->RepObjectiveValue2);
            // warning
        }

        if (!qinfo->RequiredMinRepFaction && qinfo->RequiredMinRepValue > 0)
        {
            sLog->outErrorDb("Quest %u has `RequiredMinRepValue` = %d but `RequiredMinRepFaction` is 0, value has no effect", qinfo->GetQuestId(), qinfo->RequiredMinRepValue);
            // warning
        }

        if (!qinfo->RequiredMaxRepFaction && qinfo->RequiredMaxRepValue > 0)
        {
            sLog->outErrorDb("Quest %u has `RequiredMaxRepValue` = %d but `RequiredMaxRepFaction` is 0, value has no effect", qinfo->GetQuestId(), qinfo->RequiredMaxRepValue);
            // warning
        }

        if (qinfo->CharTitleId && !sCharTitlesStore.LookupEntry(qinfo->CharTitleId))
        {
            sLog->outErrorDb("Quest %u has `CharTitleId` = %u but CharTitle Id %u does not exist, quest can't be rewarded with title.", qinfo->GetQuestId(), qinfo->GetCharTitleId(), qinfo->GetCharTitleId());
            qinfo->CharTitleId = 0;
            // quest can't reward this title
        }

        if (qinfo->SrcItemId)
        {
            if (!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
            {
                sLog->outErrorDb("Quest %u has `SrcItemId` = %u but item with entry %u does not exist, quest can't be done.", qinfo->GetQuestId(), qinfo->SrcItemId, qinfo->SrcItemId);
                qinfo->SrcItemId = 0;          // quest can't be done for this requirement
            }
            else if (qinfo->SrcItemCount == 0)
            {
                sLog->outErrorDb("Quest %u has `SrcItemId` = %u but `SrcItemCount` = 0, set to 1 but need fix in DB.", qinfo->GetQuestId(), qinfo->SrcItemId);
                qinfo->SrcItemCount = 1;          // update to 1 for allow quest work for backward compatibility with DB
            }
        }
        else if (qinfo->SrcItemCount > 0)
        {
            sLog->outErrorDb("Quest %u has `SrcItemId` = 0 but `SrcItemCount` = %u, useless value.", qinfo->GetQuestId(), qinfo->SrcItemCount);
            qinfo->SrcItemCount = 0;          // no quest work changes in fact
        }

        if (qinfo->SrcSpell)
        {
            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
            if (!spellInfo)
            {
                sLog->outErrorDb("Quest %u has `SrcSpell` = %u but spell %u doesn't exist, quest can't be done.", qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
                qinfo->SrcSpell = 0;          // quest can't be done for this requirement
            }
            else if (!SpellMgr::IsSpellValid(spellInfo))
            {
                sLog->outErrorDb("Quest %u has `SrcSpell` = %u but spell %u is broken, quest can't be done.", qinfo->GetQuestId(), qinfo->SrcSpell, qinfo->SrcSpell);
                qinfo->SrcSpell = 0;          // quest can't be done for this requirement
            }
        }

        for (uint8 j = 0; j < QUEST_ITEM_OBJECTIVES_COUNT; ++j)
        {
            uint32 id = qinfo->ReqItemId[j];
            if (id)
            {
                if (qinfo->ReqItemCount[j] == 0)
                {
                    sLog->outErrorDb("Quest %u has `ReqItemId%d` = %u but `ReqItemCount%d` = 0, quest can't be done.", qinfo->GetQuestId(), j + 1, id, j + 1);
                    // no changes, quest can't be done for this requirement
                }

                qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_DELIVER);

                if (!sItemStorage.LookupEntry<ItemPrototype>(id))
                {
                    sLog->outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, id);
                    qinfo->ReqItemCount[j] = 0;          // prevent incorrect work of quest
                }
            }
            else if (qinfo->ReqItemCount[j] > 0)
            {
                sLog->outErrorDb("Quest %u has `ReqItemId%d` = 0 but `ReqItemCount%d` = %u, quest can't be done.", qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqItemCount[j]);
                qinfo->ReqItemCount[j] = 0;          // prevent incorrect work of quest
            }
        }

        for (uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j)
        {
            uint32 id = qinfo->ReqSourceId[j];
            if (id)
            {
                if (!sItemStorage.LookupEntry<ItemPrototype>(id))
                {
                    sLog->outErrorDb("Quest %u has `ReqSourceId%d` = %u but item with entry %u does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, id);
                    // no changes, quest can't be done for this requirement
                }
            }
            else
            {
                if (qinfo->ReqSourceCount[j] > 0)
                {
                    sLog->outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceCount%d` = %u.", qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqSourceCount[j]);
                    // no changes, quest ignore this data
                }
            }
        }

        for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
        {
            uint32 id = qinfo->ReqSpell[j];
            if (id)
            {
                SpellEntry const* spellInfo = sSpellStore.LookupEntry(id);
                if (!spellInfo)
                {
                    sLog->outErrorDb("Quest %u has `ReqSpellCast%d` = %u but spell %u does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, id);
                    continue;
                }

                if (!qinfo->ReqCreatureOrGOId[j])
                {
                    bool found = false;
                    for (uint8 k = 0; k < MAX_SPELL_EFFECTS; ++k)
                    {
                        if ((spellInfo->Effect[k] == SPELL_EFFECT_QUEST_COMPLETE && uint32(spellInfo->EffectMiscValue[k]) == qinfo->QuestId) || spellInfo->Effect[k] == SPELL_EFFECT_SEND_EVENT)
                        {
                            found = true;
                            break;
                        }
                    }

                    if (found)
                    {
                        if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
                        {
                            sLog->outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT for quest %u and ReqCreatureOrGOId%d = 0, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.", spellInfo->Id, qinfo->QuestId, j + 1, j + 1);

                            // this will prevent quest completing without objective
                            const_cast<Quest*>(qinfo)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
                        }
                    }
                    else
                    {
                        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.", qinfo->GetQuestId(), j + 1, id, j + 1, id);
                        // no changes, quest can't be done for this requirement
                    }
                }
            }
        }

        for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
        {
            int32 id = qinfo->ReqCreatureOrGOId[j];
            if (id < 0 && !sGOStorage.LookupEntry<GameObjectInfo>(-id))
            {
                sLog->outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but gameobject %u does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, uint32(-id));
                qinfo->ReqCreatureOrGOId[j] = 0;          // quest can't be done for this requirement
            }

            if (id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
            {
                sLog->outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.", qinfo->GetQuestId(), j + 1, id, uint32(id));
                qinfo->ReqCreatureOrGOId[j] = 0;          // quest can't be done for this requirement
            }

            if (id)
            {
                // In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast

                qinfo->SetSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAG_KILL_OR_CAST | QUEST_SPECIAL_FLAG_SPEAKTO));

                if (!qinfo->ReqCreatureOrGOCount[j])
                {
                    sLog->outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.", qinfo->GetQuestId(), j + 1, id, j + 1);
                    // no changes, quest can be incorrectly done, but we already report this
                }
            }
            else if (qinfo->ReqCreatureOrGOCount[j] > 0)
            {
                sLog->outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = 0 but `ReqCreatureOrGOCount%d` = %u.", qinfo->GetQuestId(), j + 1, j + 1, qinfo->ReqCreatureOrGOCount[j]);
                // no changes, quest ignore this data
            }
        }

        for (uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j)
        {
            uint32 id = qinfo->RewChoiceItemId[j];
            if (id)
            {
                if (!sItemStorage.LookupEntry<ItemPrototype>(id))
                {
                    sLog->outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.", qinfo->GetQuestId(), j + 1, id, id);
                    qinfo->RewChoiceItemId[j] = 0;          // no changes, quest will not reward this
                }

                if (!qinfo->RewChoiceItemCount[j])
                {
                    sLog->outErrorDb("Quest %u has `RewChoiceItemId%d` = %u but `RewChoiceItemCount%d` = 0, quest can't be done.", qinfo->GetQuestId(), j + 1, id, j + 1);
                    // no changes, quest can't be done
                }
            }
            else if (qinfo->RewChoiceItemCount[j] > 0)
            {
                sLog->outErrorDb("Quest %u has `RewChoiceItemId%d` = 0 but `RewChoiceItemCount%d` = %u.", qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewChoiceItemCount[j]);
                // no changes, quest ignore this data
            }
        }

        for (uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j)
        {
            uint32 id = qinfo->RewItemId[j];
            if (id)
            {
                if (!sItemStorage.LookupEntry<ItemPrototype>(id))
                {
                    sLog->outErrorDb("Quest %u has `RewItemId%d` = %u but item with entry %u does not exist, quest will not reward this item.", qinfo->GetQuestId(), j + 1, id, id);
                    qinfo->RewItemId[j] = 0;          // no changes, quest will not reward this item
                }

                if (!qinfo->RewItemCount[j])
                {
                    sLog->outErrorDb("Quest %u has `RewItemId%d` = %u but `RewItemCount%d` = 0, quest will not reward this item.", qinfo->GetQuestId(), j + 1, id, j + 1);
                    // no changes
                }
            }
            else if (qinfo->RewItemCount[j] > 0)
            {
                sLog->outErrorDb("Quest %u has `RewItemId%d` = 0 but `RewItemCount%d` = %u.", qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewItemCount[j]);
                // no changes, quest ignore this data
            }
        }

        for (uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
        {
            if (qinfo->RewRepFaction[j])
            {
                if (abs(qinfo->RewRepValueId[j]) > 9)
                {
                    sLog->outErrorDb("Quest %u has RewRepValueId%d = %i. That is outside the range of valid values (-9 to 9).", qinfo->GetQuestId(), j + 1, qinfo->RewRepValueId[j]);
                }
                if (!sFactionStore.LookupEntry(qinfo->RewRepFaction[j]))
                {
                    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.", qinfo->GetQuestId(), j + 1, qinfo->RewRepFaction[j], qinfo->RewRepFaction[j]);
                    qinfo->RewRepFaction[j] = 0;          // quest will not reward this
                }
            }

            else if (qinfo->RewRepValue[j] != 0)
            {
                sLog->outErrorDb("Quest %u has `RewRepFaction%d` = 0 but `RewRepValue%d` = %i.", qinfo->GetQuestId(), j + 1, j + 1, qinfo->RewRepValue[j]);
                // no changes, quest ignore this data
            }
        }

        if (qinfo->RewSpell)
        {
            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpell);

            if (!spellInfo)
            {
                sLog->outErrorDb("Quest %u has `RewSpell` = %u but spell %u does not exist, spell removed as display reward.", qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
                qinfo->RewSpell = 0;          // no spell reward will display for this quest
            }

            else if (!SpellMgr::IsSpellValid(spellInfo))
            {
                sLog->outErrorDb("Quest %u has `RewSpell` = %u but spell %u is broken, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
                qinfo->RewSpell = 0;          // no spell reward will display for this quest
            }

            else if (GetTalentSpellCost(qinfo->RewSpell))
            {
                sLog->outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpell, qinfo->RewSpell);
                qinfo->RewSpell = 0;          // no spell reward will display for this quest
            }
        }

        if (qinfo->RewSpellCast > 0)
        {
            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellCast);

            if (!spellInfo)
            {
                sLog->outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u does not exist, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
                qinfo->RewSpellCast = 0;          // no spell will be casted on player
            }

            else if (!SpellMgr::IsSpellValid(spellInfo))
            {
                sLog->outErrorDb("Quest %u has `RewSpellCast` = %u but spell %u is broken, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
                qinfo->RewSpellCast = 0;          // no spell will be casted on player
            }

            else if (GetTalentSpellCost(qinfo->RewSpellCast))
            {
                sLog->outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpellCast, qinfo->RewSpellCast);
                qinfo->RewSpellCast = 0;          // no spell will be casted on player
            }
        }

        if (qinfo->RewSpellHiddenCast > 0)
        {
            SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->RewSpellHiddenCast);

            if (!spellInfo)
            {
                sLog->outErrorDb("Quest %u has `RewSpellHiddenCast` = %u but spell %u does not exist, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpellHiddenCast, qinfo->RewSpellHiddenCast);
                qinfo->RewSpellHiddenCast = 0;          // no spell will be casted on player
            }

            else if (!SpellMgr::IsSpellValid(spellInfo))
            {
                sLog->outErrorDb("Quest %u has `RewSpellHiddenCast` = %u but spell %u is broken, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpellHiddenCast, qinfo->RewSpellHiddenCast);
                qinfo->RewSpellHiddenCast = 0;          // no spell will be casted on player
            }

            else if (GetTalentSpellCost(qinfo->RewSpellHiddenCast))
            {
                sLog->outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.", qinfo->GetQuestId(), qinfo->RewSpellHiddenCast, qinfo->RewSpellHiddenCast);
                qinfo->RewSpellHiddenCast = 0;          // no spell will be casted on player
            }
        }

        if (qinfo->RewMailTemplateId)
        {
            if (!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
            {
                sLog->outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template  %u does not exist, quest will not have a mail reward.", qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId);
                qinfo->RewMailTemplateId = 0;          // no mail will send to player
                qinfo->RewMailDelaySecs = 0;          // no mail will send to player
            }
            else if (usedMailTemplates.find(qinfo->RewMailTemplateId) != usedMailTemplates.end())
            {
                std::map<uint32, uint32>::const_iterator used_mt_itr = usedMailTemplates.find(qinfo->RewMailTemplateId);
                sLog->outErrorDb("Quest %u has `RewMailTemplateId` = %u but mail template  %u already used for quest %u, quest will not have a mail reward.", qinfo->GetQuestId(), qinfo->RewMailTemplateId, qinfo->RewMailTemplateId, used_mt_itr->second);
                qinfo->RewMailTemplateId = 0;          // no mail will send to player
                qinfo->RewMailDelaySecs = 0;          // no mail will send to player
            }
            else
                usedMailTemplates[qinfo->RewMailTemplateId] = qinfo->GetQuestId();
        }

        if (qinfo->NextQuestInChain)
        {
            QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
            if (qNextItr == mQuestTemplates.end())
            {
                sLog->outErrorDb("Quest %u has `NextQuestInChain` = %u but quest %u does not exist, quest chain will not work.", qinfo->GetQuestId(), qinfo->NextQuestInChain, qinfo->NextQuestInChain);
                qinfo->NextQuestInChain = 0;
            }
            else
                qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
        }

        // fill additional data stores
        if (qinfo->PrevQuestId)
        {
            if (mQuestTemplates.find(abs(qinfo->GetPrevQuestId())) == mQuestTemplates.end())
            {
                sLog->outErrorDb("Quest %d has PrevQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetPrevQuestId());
            }
            else
            {
                qinfo->prevQuests.push_back(qinfo->PrevQuestId);
            }
        }

        if (qinfo->NextQuestId)
        {
            QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId()));
            if (qNextItr == mQuestTemplates.end())
            {
                sLog->outErrorDb("Quest %d has NextQuestId %i, but no such quest", qinfo->GetQuestId(), qinfo->GetNextQuestId());
            }
            else
            {
                int32 signedQuestId = qinfo->NextQuestId < 0 ? -int32(qinfo->GetQuestId()) : int32(qinfo->GetQuestId());
                qNextItr->second->prevQuests.push_back(signedQuestId);
            }
        }

        if (qinfo->ExclusiveGroup)
            mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
        if (qinfo->LimitTime)
            qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAG_TIMED);
    }

    // check QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
    for (uint32 i = 0; i < sSpellStore.GetNumRows(); ++i)
    {
        SpellEntry const *spellInfo = sSpellStore.LookupEntry(i);
        if (!spellInfo)
            continue;

        for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
        {
            if (spellInfo->Effect[j] != SPELL_EFFECT_QUEST_COMPLETE)
                continue;

            uint32 quest_id = spellInfo->EffectMiscValue[j];

            Quest const *quest = GetQuestTemplate(quest_id);

            // some quest referenced in spells not exist (outdated spells)
            if (!quest)
                continue;

            if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
            {
                sLog->outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id);

                // this will prevent quest completing without objective
                const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);
            }
        }
    }

    sLog->outString();
    sLog->outString(">> Loaded %lu quests definitions", (unsigned long) mQuestTemplates.size());
}

void ObjectMgr::LoadQuestLocales ()
{
    mQuestLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT entry, "
            "Title_loc1, Details_loc1, Objectives_loc1, OfferRewardText_loc1, RequestItemsText_loc1, EndText_loc1, CompletedText_loc1, ObjectiveText1_loc1, ObjectiveText2_loc1, ObjectiveText3_loc1, ObjectiveText4_loc1, "
            "Title_loc2, Details_loc2, Objectives_loc2, OfferRewardText_loc2, RequestItemsText_loc2, EndText_loc2, CompletedText_loc2, ObjectiveText1_loc2, ObjectiveText2_loc2, ObjectiveText3_loc2, ObjectiveText4_loc2, "
            "Title_loc3, Details_loc3, Objectives_loc3, OfferRewardText_loc3, RequestItemsText_loc3, EndText_loc3, CompletedText_loc3, ObjectiveText1_loc3, ObjectiveText2_loc3, ObjectiveText3_loc3, ObjectiveText4_loc3, "
            "Title_loc4, Details_loc4, Objectives_loc4, OfferRewardText_loc4, RequestItemsText_loc4, EndText_loc4, CompletedText_loc4, ObjectiveText1_loc4, ObjectiveText2_loc4, ObjectiveText3_loc4, ObjectiveText4_loc4, "
            "Title_loc5, Details_loc5, Objectives_loc5, OfferRewardText_loc5, RequestItemsText_loc5, EndText_loc5, CompletedText_loc5, ObjectiveText1_loc5, ObjectiveText2_loc5, ObjectiveText3_loc5, ObjectiveText4_loc5, "
            "Title_loc6, Details_loc6, Objectives_loc6, OfferRewardText_loc6, RequestItemsText_loc6, EndText_loc6, CompletedText_loc6, ObjectiveText1_loc6, ObjectiveText2_loc6, ObjectiveText3_loc6, ObjectiveText4_loc6, "
            "Title_loc7, Details_loc7, Objectives_loc7, OfferRewardText_loc7, RequestItemsText_loc7, EndText_loc7, CompletedText_loc7, ObjectiveText1_loc7, ObjectiveText2_loc7, ObjectiveText3_loc7, ObjectiveText4_loc7, "
            "Title_loc8, Details_loc8, Objectives_loc8, OfferRewardText_loc8, RequestItemsText_loc8, EndText_loc8, CompletedText_loc8, ObjectiveText1_loc8, ObjectiveText2_loc8, ObjectiveText3_loc8, ObjectiveText4_loc8"
            " FROM locales_quest");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        QuestLocale& data = mQuestLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            LocaleConstant locale = (LocaleConstant) i;
            std::string str = fields[1 + 11 * (i - 1)].GetString();
            AddLocaleString(str, locale, data.Title);

            str = fields[1 + 11 * (i - 1) + 1].GetString();
            AddLocaleString(str, locale, data.Details);

            str = fields[1 + 11 * (i - 1) + 2].GetString();
            AddLocaleString(str, locale, data.Objectives);

            str = fields[1 + 11 * (i - 1) + 3].GetString();
            AddLocaleString(str, locale, data.OfferRewardText);

            str = fields[1 + 11 * (i - 1) + 4].GetString();
            AddLocaleString(str, locale, data.RequestItemsText);

            str = fields[1 + 11 * (i - 1) + 5].GetString();
            AddLocaleString(str, locale, data.EndText);

            str = fields[1 + 11 * (i - 1) + 6].GetString();
            AddLocaleString(str, locale, data.CompletedText);

            for (uint8 k = 0; k < 4; ++k)
            {
                str = fields[1 + 11 * (i - 1) + 7 + k].GetString();
                AddLocaleString(str, locale, data.ObjectiveText[k]);
            }
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu Quest locale strings", (unsigned long) mQuestLocaleMap.size());
}

void ObjectMgr::LoadScripts (ScriptsType type)
{
    ScriptMapMap *scripts = GetScriptsMapByType(type);
    if (!scripts)
        return;

    std::string tableName = GetScriptsTableNameByType(type);
    if (tableName.empty())
        return;

    if (sWorld->IsScriptScheduled())          // function don't must be called in time scripts use.
        return;

    sLog->outString("%s :", tableName.c_str());

    scripts->clear();          // need for reload support

    bool isSpellScriptTable = (type == SCRIPTS_SPELL);
    char buff[125];
    sprintf(buff, "SELECT id, delay, command, datalong, datalong2, dataint, x, y, z, o%s FROM %s", isSpellScriptTable ? ", effIndex" : "", tableName.c_str());
    QueryResult result = WorldDatabase.Query(buff);

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u script definitions", count);
        return;
    }

    do
    {
        Field *fields = result->Fetch();
        ScriptInfo tmp;
        tmp.type = type;
        tmp.id = fields[0].GetUInt32();
        if (isSpellScriptTable)
            tmp.id |= fields[10].GetUInt8() << 24;
        tmp.delay = fields[1].GetUInt32();
        tmp.command = ScriptCommands(fields[2].GetUInt32());
        tmp.Raw.nData[0] = fields[3].GetUInt32();
        tmp.Raw.nData[1] = fields[4].GetUInt32();
        tmp.Raw.nData[2] = fields[5].GetInt32();
        tmp.Raw.fData[0] = fields[6].GetFloat();
        tmp.Raw.fData[1] = fields[7].GetFloat();
        tmp.Raw.fData[2] = fields[8].GetFloat();
        tmp.Raw.fData[3] = fields[9].GetFloat();

        // generic command args check
        switch (tmp.command)
        {
        case SCRIPT_COMMAND_TALK:
        {
            if (tmp.Talk.ChatType > CHAT_TYPE_WHISPER && tmp.Talk.ChatType != CHAT_MSG_RAID_BOSS_WHISPER)
            {
                sLog->outErrorDb("Table `%s` has invalid talk type (datalong = %u) in SCRIPT_COMMAND_TALK for script id %u", tableName.c_str(), tmp.Talk.ChatType, tmp.id);
                continue;
            }
            if (!tmp.Talk.TextID)
            {
                sLog->outErrorDb("Table `%s` has invalid talk text id (dataint = %i) in SCRIPT_COMMAND_TALK for script id %u", tableName.c_str(), tmp.Talk.TextID, tmp.id);
                continue;
            }
            if (tmp.Talk.TextID < MIN_DB_SCRIPT_STRING_ID || tmp.Talk.TextID >= MAX_DB_SCRIPT_STRING_ID)
            {
                sLog->outErrorDb("Table `%s` has out of range text id (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u", tableName.c_str(), tmp.Talk.TextID, MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID, tmp.id);
                continue;
            }

            break;
        }

        case SCRIPT_COMMAND_EMOTE:
        {
            if (!sEmotesStore.LookupEntry(tmp.Emote.EmoteID))
            {
                sLog->outErrorDb("Table `%s` has invalid emote id (datalong = %u) in SCRIPT_COMMAND_EMOTE for script id %u", tableName.c_str(), tmp.Emote.EmoteID, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_TELEPORT_TO:
        {
            if (!sMapStore.LookupEntry(tmp.TeleportTo.MapID))
            {
                sLog->outErrorDb("Table `%s` has invalid map (Id: %u) in SCRIPT_COMMAND_TELEPORT_TO for script id %u", tableName.c_str(), tmp.TeleportTo.MapID, tmp.id);
                continue;
            }

            if (!Trinity::IsValidMapCoord(tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation))
            {
                sLog->outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TELEPORT_TO for script id %u", tableName.c_str(), tmp.TeleportTo.DestX, tmp.TeleportTo.DestY, tmp.TeleportTo.DestZ, tmp.TeleportTo.Orientation, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_QUEST_EXPLORED:
        {
            Quest const* quest = GetQuestTemplate(tmp.QuestExplored.QuestID);
            if (!quest)
            {
                sLog->outErrorDb("Table `%s` has invalid quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u", tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
                continue;
            }

            if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
            {
                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_SPECIAL_FLAG_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.", tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);

                // this will prevent quest completing without objective
                const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);

                // continue; - quest objective requirement set and command can be allowed
            }

            if (float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
            {
                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.c_str(), tmp.QuestExplored.Distance, tmp.id);
                continue;
            }

            if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
            {
                sLog->outErrorDb("Table `%s` has too large distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, max distance is %f or 0 for disable distance check", tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, DEFAULT_VISIBILITY_DISTANCE);
                continue;
            }

            if (tmp.QuestExplored.Distance && float(tmp.QuestExplored.Distance) < INTERACTION_DISTANCE)
            {
                sLog->outErrorDb("Table `%s` has too small distance (%u) for exploring objective complete in `datalong2` in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, min distance is %f or 0 for disable distance check", tableName.c_str(), tmp.QuestExplored.Distance, tmp.id, INTERACTION_DISTANCE);
                continue;
            }

            break;
        }

        case SCRIPT_COMMAND_KILL_CREDIT:
        {
            if (!GetCreatureTemplate(tmp.KillCredit.CreatureEntry))
            {
                sLog->outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_KILL_CREDIT for script id %u", tableName.c_str(), tmp.KillCredit.CreatureEntry, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
        {
            GameObjectData const* data = GetGOData(tmp.RespawnGameobject.GOGuid);
            if (!data)
            {
                sLog->outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tableName.c_str(), tmp.RespawnGameobject.GOGuid, tmp.id);
                continue;
            }

            GameObjectInfo const* info = GetGameObjectInfo(data->id);
            if (!info)
            {
                sLog->outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tableName.c_str(), tmp.RespawnGameobject.GOGuid, data->id, tmp.id);
                continue;
            }

            if (info->type == GAMEOBJECT_TYPE_FISHINGNODE || info->type == GAMEOBJECT_TYPE_FISHINGHOLE || info->type == GAMEOBJECT_TYPE_DOOR || info->type == GAMEOBJECT_TYPE_BUTTON || info->type == GAMEOBJECT_TYPE_TRAP)
            {
                sLog->outErrorDb("Table `%s` have gameobject type (%u) unsupported by command SCRIPT_COMMAND_RESPAWN_GAMEOBJECT for script id %u", tableName.c_str(), info->id, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
        {
            if (!Trinity::IsValidMapCoord(tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation))
            {
                sLog->outErrorDb("Table `%s` has invalid coordinates (X: %f Y: %f Z: %f O: %f) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u", tableName.c_str(), tmp.TempSummonCreature.PosX, tmp.TempSummonCreature.PosY, tmp.TempSummonCreature.PosZ, tmp.TempSummonCreature.Orientation, tmp.id);
                continue;
            }

            if (!GetCreatureTemplate(tmp.TempSummonCreature.CreatureEntry))
            {
                sLog->outErrorDb("Table `%s` has invalid creature (Entry: %u) in SCRIPT_COMMAND_TEMP_SUMMON_CREATURE for script id %u", tableName.c_str(), tmp.TempSummonCreature.CreatureEntry, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_OPEN_DOOR:
        case SCRIPT_COMMAND_CLOSE_DOOR:
        {
            GameObjectData const* data = GetGOData(tmp.ToggleDoor.GOGuid);
            if (!data)
            {
                sLog->outErrorDb("Table `%s` has invalid gameobject (GUID: %u) in %s for script id %u", tableName.c_str(), tmp.ToggleDoor.GOGuid, GetScriptCommandName(tmp.command).c_str(), tmp.id);
                continue;
            }

            GameObjectInfo const* info = GetGameObjectInfo(data->id);
            if (!info)
            {
                sLog->outErrorDb("Table `%s` has gameobject with invalid entry (GUID: %u Entry: %u) in %s for script id %u", tableName.c_str(), tmp.ToggleDoor.GOGuid, data->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
                continue;
            }

            if (info->type != GAMEOBJECT_TYPE_DOOR)
            {
                sLog->outErrorDb("Table `%s` has gameobject type (%u) non supported by command %s for script id %u", tableName.c_str(), info->id, GetScriptCommandName(tmp.command).c_str(), tmp.id);
                continue;
            }

            break;
        }

        case SCRIPT_COMMAND_REMOVE_AURA:
        {
            if (!sSpellStore.LookupEntry(tmp.RemoveAura.SpellID))
            {
                sLog->outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u", tableName.c_str(), tmp.RemoveAura.SpellID, tmp.id);
                continue;
            }
            if (tmp.RemoveAura.Flags & ~0x1)          // 1 bits (0, 1)
            {
                sLog->outErrorDb("Table `%s` using unknown flags in datalong2 (%u) in SCRIPT_COMMAND_REMOVE_AURA for script id %u", tableName.c_str(), tmp.RemoveAura.Flags, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_CAST_SPELL:
        {
            if (!sSpellStore.LookupEntry(tmp.CastSpell.SpellID))
            {
                sLog->outErrorDb("Table `%s` using non-existent spell (id: %u) in SCRIPT_COMMAND_CAST_SPELL for script id %u", tableName.c_str(), tmp.CastSpell.SpellID, tmp.id);
                continue;
            }
            if (tmp.CastSpell.Flags > 4)          // targeting type
            {
                sLog->outErrorDb("Table `%s` using unknown target in datalong2 (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u", tableName.c_str(), tmp.CastSpell.Flags, tmp.id);
                continue;
            }
            if (tmp.CastSpell.Flags != 4 && tmp.CastSpell.CreatureEntry & ~0x1)          // 1 bit (0, 1)
            {
                sLog->outErrorDb("Table `%s` using unknown flags in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u", tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
                continue;
            }
            else if (tmp.CastSpell.Flags == 4 && !GetCreatureTemplate(tmp.CastSpell.CreatureEntry))
            {
                sLog->outErrorDb("Table `%s` using invalid creature entry in dataint (%u) in SCRIPT_COMMAND_CAST_SPELL for script id %u", tableName.c_str(), tmp.CastSpell.CreatureEntry, tmp.id);
                continue;
            }
            break;
        }

        case SCRIPT_COMMAND_CREATE_ITEM:
        {
            if (!ObjectMgr::GetItemPrototype(tmp.CreateItem.ItemEntry))
            {
                sLog->outErrorDb("Table `%s` has nonexistent item (entry: %u) in SCRIPT_COMMAND_CREATE_ITEM for script id %u", tableName.c_str(), tmp.CreateItem.ItemEntry, tmp.id);
                continue;
            }
            if (!tmp.CreateItem.Amount)
            {
                sLog->outErrorDb("Table `%s` SCRIPT_COMMAND_CREATE_ITEM but amount is %u for script id %u", tableName.c_str(), tmp.CreateItem.Amount, tmp.id);
                continue;
            }
            break;
        }
        default:
            break;
        }

        if (scripts->find(tmp.id) == scripts->end())
        {
            ScriptMap emptyMap;
            (*scripts)[tmp.id] = emptyMap;
        }
        (*scripts)[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u script definitions", count);
}

void ObjectMgr::LoadGameObjectScripts ()
{
    LoadScripts(SCRIPTS_GAMEOBJECT);

    // check ids
    for (ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
    {
        if (!GetGOData(itr->first))
            sLog->outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id", itr->first);
    }
}

void ObjectMgr::LoadQuestEndScripts ()
{
    LoadScripts(SCRIPTS_QUEST_END);

    // check ids
    for (ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
    {
        if (!GetQuestTemplate(itr->first))
            sLog->outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id", itr->first);
    }
}

void ObjectMgr::LoadQuestStartScripts ()
{
    LoadScripts(SCRIPTS_QUEST_START);

    // check ids
    for (ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
    {
        if (!GetQuestTemplate(itr->first))
            sLog->outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id", itr->first);
    }
}

void ObjectMgr::LoadSpellScripts ()
{
    LoadScripts(SCRIPTS_SPELL);

    // check ids
    for (ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
    {
        uint32 spellId = uint32(itr->first) & 0x00FFFFFF;
        SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);

        if (!spellInfo)
        {
            sLog->outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id", spellId);
            continue;
        }

        uint8 i = (uint8) ((uint32(itr->first) >> 24) & 0x000000FF);
        //check for correct spellEffect
        if (!spellInfo->Effect[i] || (spellInfo->Effect[i] != SPELL_EFFECT_SCRIPT_EFFECT && spellInfo->Effect[i] != SPELL_EFFECT_DUMMY))
            sLog->outErrorDb("Table `spell_scripts` - spell %u effect %u is not SPELL_EFFECT_SCRIPT_EFFECT or SPELL_EFFECT_DUMMY", spellId, i);
    }
}

void ObjectMgr::LoadEventScripts ()
{
    LoadScripts(SCRIPTS_EVENT);

    std::set<uint32> evt_scripts;
    // Load all possible script entries from gameobjects
    for (uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
    {
        if (GameObjectInfo const * goInfo = sGOStorage.LookupEntry<GameObjectInfo>(i))
            if (uint32 eventId = goInfo->GetEventScriptId())
                evt_scripts.insert(eventId);
    }
    // Load all possible script entries from spells
    for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
    {
        SpellEntry const * spell = sSpellStore.LookupEntry(i);
        if (spell)
        {
            for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
            {
                if (spell->Effect[j] == SPELL_EFFECT_SEND_EVENT)
                {
                    if (spell->EffectMiscValue[j])
                        evt_scripts.insert(spell->EffectMiscValue[j]);
                }
            }
        }
    }

    for (size_t path_idx = 0; path_idx < sTaxiPathNodesByPath.size(); ++path_idx)
    {
        for (size_t node_idx = 0; node_idx < sTaxiPathNodesByPath[path_idx].size(); ++node_idx)
        {
            TaxiPathNodeEntry const& node = sTaxiPathNodesByPath[path_idx][node_idx];

            if (node.arrivalEventID)
                evt_scripts.insert(node.arrivalEventID);

            if (node.departureEventID)
                evt_scripts.insert(node.departureEventID);
        }
    }

    // Then check if all scripts are in above list of possible script entries
    for (ScriptMapMap::const_iterator itr = sEventScripts.begin(); itr != sEventScripts.end(); ++itr)
    {
        std::set<uint32>::const_iterator itr2 = evt_scripts.find(itr->first);
        if (itr2 == evt_scripts.end())
            sLog->outErrorDb("Table `event_scripts` has script (Id: %u) not referring to any gameobject_template type 10 data2 field, type 3 data6 field, type 13 data 2 field or any spell effect %u", itr->first, SPELL_EFFECT_SEND_EVENT);
    }
}

//Load WP Scripts
void ObjectMgr::LoadWaypointScripts ()
{
    LoadScripts(SCRIPTS_WAYPOINT);

    std::set<uint32> actionSet;

    for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
        actionSet.insert(itr->first);

    QueryResult result = WorldDatabase.PQuery("SELECT DISTINCT(`action`) FROM waypoint_data");
    if (result)
    {
        do
        {
            Field *fields = result->Fetch();
            uint32 action = fields[0].GetUInt32();

            actionSet.erase(action);
        }
        while (result->NextRow());
    }

    for (std::set<uint32>::iterator itr = actionSet.begin(); itr != actionSet.end(); ++itr)
        sLog->outErrorDb("There is no waypoint which links to the waypoint script %u", *itr);
}

void ObjectMgr::LoadSpellScriptNames ()
{
    mSpellScripts.clear();          // need for reload case
    QueryResult result = WorldDatabase.Query("SELECT spell_id, ScriptName FROM spell_script_names");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u spell script names", count);
        return;
    }

    do
    {
        ++count;

        Field *fields = result->Fetch();

        int32 spellId = fields[0].GetInt32();
        const char *scriptName = fields[1].GetCString();

        bool allRanks = false;
        if (spellId <= 0)
        {
            allRanks = true;
            spellId = -spellId;
        }

        SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId);
        if (!spellEntry)
        {
            sLog->outErrorDb("Scriptname:`%s` spell (spell_id:%d) does not exist in `Spell.dbc`.", scriptName, fields[0].GetInt32());
            continue;
        }

        if (allRanks)
        {
            if (sSpellMgr->GetFirstSpellInChain(spellId) != uint32(spellId))
            {
                sLog->outErrorDb("Scriptname:`%s` spell (spell_id:%d) is not first rank of spell.", scriptName, fields[0].GetInt32());
                continue;
            }
            while (spellId)
            {
                mSpellScripts.insert(SpellScriptsMap::value_type(spellId, GetScriptId(scriptName)));
                spellId = sSpellMgr->GetNextSpellInChain(spellId);
            }
        }
        else
            mSpellScripts.insert(SpellScriptsMap::value_type(spellId, GetScriptId(scriptName)));
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u spell script names", count);
}

void ObjectMgr::ValidateSpellScripts ()
{
    if (mSpellScripts.empty())
    {
        sLog->outString();
        sLog->outString(">> Validation done");
        return;
    }

    for (SpellScriptsMap::iterator itr = mSpellScripts.begin(); itr != mSpellScripts.end();)
    {
        SpellEntry const * spellEntry = sSpellStore.LookupEntry(itr->first);
        std::vector < std::pair<SpellScriptLoader *, SpellScriptsMap::iterator> > SpellScriptLoaders;
        sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
        itr = mSpellScripts.upper_bound(itr->first);

        for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsMap::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
        {
            SpellScript * spellScript = sitr->first->GetSpellScript();
            AuraScript * auraScript = sitr->first->GetAuraScript();
            bool valid = true;
            if (!spellScript && !auraScript)
            {
                sLog->outError("TSCR: Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second));
                valid = false;
            }
            if (spellScript)
            {
                spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
                spellScript->_Register();
                if (!spellScript->_Validate(spellEntry))
                    valid = false;
                delete spellScript;
            }
            if (auraScript)
            {
                auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
                auraScript->_Register();
                if (!auraScript->_Validate(spellEntry))
                    valid = false;
                delete auraScript;
            }
            if (!valid)
                mSpellScripts.erase(sitr->second);
        }
    }

    sLog->outString();
    sLog->outString(">> Validation done");
}

void ObjectMgr::LoadGossipScripts ()
{
    LoadScripts(SCRIPTS_GOSSIP);

    // checks are done in LoadGossipMenuItems
}

void ObjectMgr::LoadPageTexts ()
{
    sPageTextStore.Free();          // for reload case

    sPageTextStore.Load();
    sLog->outString(">> Loaded %u page texts", sPageTextStore.RecordCount);
    sLog->outString();

    for (uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
    {
        // check data correctness
        PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
        if (!page)
            continue;

        if (page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
        {
            sLog->outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i, page->Next_Page);
            continue;
        }

        // detect circular reference
        std::set<uint32> checkedPages;
        for (PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
        {
            if (!pageItr->Next_Page)
                break;
            checkedPages.insert(pageItr->Page_ID);
            if (checkedPages.find(pageItr->Next_Page) != checkedPages.end())
            {
                std::ostringstream ss;
                ss << "The text page(s) ";
                for (std::set<uint32>::iterator itr = checkedPages.begin(); itr != checkedPages.end(); ++itr)
                    ss << *itr << " ";
                ss << "create(s) a circular reference, which can cause the server to freeze. Changing Next_Page of page " << pageItr->Page_ID << " to 0";
                sLog->outErrorDb("%s", ss.str().c_str());
                const_cast<PageText*>(pageItr)->Next_Page = 0;
                break;
            }
        }
    }
}

void ObjectMgr::LoadPageTextLocales ()
{
    mPageTextLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT entry, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8 FROM locales_page_text");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        PageTextLocale& data = mPageTextLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            std::string str = fields[i].GetString();
            AddLocaleString(str, LocaleConstant(i), data.Text);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu PageText locale strings", (unsigned long) mPageTextLocaleMap.size());
}

struct SQLInstanceLoader: public SQLStorageLoaderBase<SQLInstanceLoader>
{
    template<class D>
    void convert_from_str (uint32 /*field_pos*/, char *src, D &dst)
    {
        dst = D(sObjectMgr->GetScriptId(src));
    }
};

void ObjectMgr::LoadInstanceTemplate ()
{
    SQLInstanceLoader loader;
    loader.Load(sInstanceTemplate);

    for (uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
    {
        InstanceTemplate* temp = const_cast<InstanceTemplate*>(ObjectMgr::GetInstanceTemplate(i));
        if (!temp)
            continue;

        if (!MapManager::IsValidMAP(temp->map, true))
            sLog->outErrorDb("ObjectMgr::LoadInstanceTemplate: bad mapid %d for template!", temp->map);

        if (!MapManager::IsValidMapCoord(temp->parent, temp->startLocX, temp->startLocY, temp->startLocZ, temp->startLocO))
        {
            sLog->outErrorDb("ObjectMgr::LoadInstanceTemplate: bad parent entrance coordinates for map id %d template!", temp->map);
            temp->parent = 0;          // will have wrong continent 0 parent, at least existed
        }
    }

    sLog->outString(">> Loaded %u Instance Template definitions", sInstanceTemplate.RecordCount);
    sLog->outString();
}

void ObjectMgr::LoadInstanceEncounters ()
{
    uint32 oldMSTime = getMSTime();

    QueryResult result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon FROM instance_encounters");
    if (!result)
    {
        sLog->outErrorDb(">> Loaded 0 instance encounters, table is empty!");
        sLog->outString();
        return;
    }

    uint32 count = 0;
    std::map<uint32, DungeonEncounterEntry const*> dungeonLastBosses;
    do
    {
        Field* fields = result->Fetch();
        uint32 entry = fields[0].GetUInt32();
        uint8 creditType = fields[1].GetUInt8();
        uint32 creditEntry = fields[2].GetUInt32();
        uint32 lastEncounterDungeon = fields[3].GetUInt32();
        DungeonEncounterEntry const* dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
        if (!dungeonEncounter)
        {
            sLog->outErrorDb("Table `instance_encounters` has an invalid encounter id %u, skipped!", entry);
            continue;
        }

        if (lastEncounterDungeon && !sLFGDungeonStore.LookupEntry(lastEncounterDungeon))
        {
            sLog->outErrorDb("Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName, lastEncounterDungeon);
            continue;
        }

        std::map<uint32, DungeonEncounterEntry const*>::const_iterator itr = dungeonLastBosses.find(lastEncounterDungeon);
        if (lastEncounterDungeon)
        {
            if (itr != dungeonLastBosses.end())
            {
                sLog->outErrorDb("Table `instance_encounters` specified encounter %u (%s) as last encounter but %u (%s) is already marked as one, skipped!", entry, dungeonEncounter->encounterName, itr->second->id, itr->second->encounterName);
                continue;
            }

            dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
        }

        switch (creditType)
        {
        case ENCOUNTER_CREDIT_KILL_CREATURE:
        {
            CreatureInfo const* creatureInfo = GetCreatureInfo(creditEntry);
            if (!creatureInfo)
            {
                sLog->outErrorDb("Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName);
                continue;
            }
            const_cast<CreatureInfo*>(creatureInfo)->flags_extra |= CREATURE_FLAG_EXTRA_DUNGEON_BOSS;
            break;
        }
        case ENCOUNTER_CREDIT_CAST_SPELL:
            if (!sSpellStore.LookupEntry(creditEntry))
            {
                sLog->outErrorDb("Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounter->encounterName);
                continue;
            }
            break;
        default:
            sLog->outErrorDb("Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounter->encounterName);
            continue;
        }

        DungeonEncounterList& encounters = mDungeonEncounters[MAKE_PAIR32(dungeonEncounter->mapId, dungeonEncounter->difficulty)];
        encounters.push_back(new DungeonEncounter(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
        ++count;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded %u instance encounters in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

GossipText const *ObjectMgr::GetGossipText (uint32 Text_ID) const
{
    GossipTextMap::const_iterator itr = mGossipText.find(Text_ID);
    if (itr != mGossipText.end())
        return &itr->second;
    return NULL;
}

void ObjectMgr::LoadGossipText ()
{
    QueryResult result = WorldDatabase.Query("SELECT * FROM npc_text");

    int count = 0;
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u npc texts", count);
        return;
    }

    int cic;

    do
    {
        ++count;
        cic = 0;

        Field *fields = result->Fetch();

        uint32 Text_ID = fields[cic++].GetUInt32();
        if (!Text_ID)
        {
            sLog->outErrorDb("Table `npc_text` has record wit reserved id 0, ignore.");
            continue;
        }

        GossipText& gText = mGossipText[Text_ID];

        for (int i = 0; i < MAX_GOSSIP_TEXT_OPTIONS; i++)
        {
            gText.Options[i].Text_0 = fields[cic++].GetString();
            gText.Options[i].Text_1 = fields[cic++].GetString();

            gText.Options[i].Language = fields[cic++].GetUInt32();
            gText.Options[i].Probability = fields[cic++].GetFloat();

            for (uint8 j = 0; j < MAX_GOSSIP_TEXT_EMOTES; ++j)
            {
                gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt32();
                gText.Options[i].Emotes[j]._Emote = fields[cic++].GetUInt32();
            }
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u npc texts", count);
}

void ObjectMgr::LoadNpcTextLocales ()
{
    mNpcTextLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT entry, "
            "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, "
            "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, "
            "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, "
            "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, "
            "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, "
            "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, "
            "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, "
            "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 "
            " FROM locales_npc_text");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        NpcTextLocale& data = mNpcTextLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            LocaleConstant locale = (LocaleConstant) i;
            for (uint8 j = 0; j < MAX_LOCALES; ++j)
            {
                std::string str0 = fields[1 + 8 * 2 * (i - 1) + 2 * j].GetString();
                AddLocaleString(str0, locale, data.Text_0[j]);

                std::string str1 = fields[1 + 8 * 2 * (i - 1) + 2 * j + 1].GetString();
                AddLocaleString(str1, locale, data.Text_1[j]);
            }
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu NpcText locale strings", (unsigned long) mNpcTextLocaleMap.size());
}

//not very fast function but it is called only once a day, or on starting-up
void ObjectMgr::ReturnOrDeleteOldMails (bool serverUp)
{
    time_t curTime = time(NULL);
    tm* lt = localtime(&curTime);
    uint64 basetime(curTime);
    sLog->outDetail("Returning mails current time: hour: %d, minute: %d, second: %d ", lt->tm_hour, lt->tm_min, lt->tm_sec);

    // Delete all old mails without item and without body immediately, if starting server
    if (!serverUp)
    {
        PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EMPTY_EXPIRED_MAIL);
        stmt->setUInt64(0, basetime);
        CharacterDatabase.Execute(stmt);
    }
    PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_GET_EXPIRED_MAIL);
    stmt->setUInt64(0, basetime);
    PreparedQueryResult result = CharacterDatabase.Query(stmt);
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty.");
        return;          // any mails need to be returned or deleted
    }

    uint32 count = 0;
    Field *fields;
    do
    {
        fields = result->Fetch();
        Mail *m = new Mail;
        m->messageID = fields[0].GetUInt32();
        m->messageType = fields[1].GetUInt8();
        m->sender = fields[2].GetUInt32();
        m->receiver = fields[3].GetUInt32();
        bool has_items = fields[4].GetBool();
        m->expire_time = time_t(fields[5].GetUInt32());
        m->deliver_time = 0;
        m->COD = fields[6].GetUInt32();
        m->checked = fields[7].GetUInt32();
        m->mailTemplateId = fields[8].GetInt16();

        Player *pl = NULL;
        if (serverUp)
            pl = GetPlayer((uint64) m->receiver);

        if (pl && pl->m_mailsLoaded)
        {          // this code will run very improbably (the time is between 4 and 5 am, in game is online a player, who has old mail
            // his in mailbox and he has already listed his mails)
            delete m;
            continue;
        }

        // Delete or return mail
        if (has_items)
        {
            stmt = CharacterDatabase.GetPreparedStatement(CHAR_GET_MAIL_ITEM_LITE);
            stmt->setUInt32(0, m->messageID);
            if (PreparedQueryResult resultItems = CharacterDatabase.Query(stmt))
            {
                do
                {
                    Field *fields2 = resultItems->Fetch();

                    uint32 item_guid_low = fields2[0].GetUInt32();
                    uint32 item_template = fields2[1].GetUInt32();

                    m->AddItem(item_guid_low, item_template);
                }
                while (resultItems->NextRow());
            }
            // if it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
            if (m->messageType != MAIL_NORMAL || (m->checked & (MAIL_CHECK_MASK_COD_PAYMENT | MAIL_CHECK_MASK_RETURNED)))
            {
                // mail open and then not returned
                for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
                {
                    PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
                    stmt->setUInt32(0, itr2->item_guid);
                    CharacterDatabase.Execute(stmt);
                }
            }
            else
            {
                // Mail will be returned
                stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_MAIL_RETURNED);
                stmt->setUInt32(0, m->receiver);
                stmt->setUInt32(1, m->sender);
                stmt->setUInt64(2, basetime + 30 * DAY);
                stmt->setUInt64(3, basetime);
                stmt->setUInt8(4, uint8(MAIL_CHECK_MASK_RETURNED));
                stmt->setUInt32(5, m->messageID);
                CharacterDatabase.Execute(stmt);
                for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
                {
                    // Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
                    stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_MAIL_ITEM_RECEIVER);
                    stmt->setUInt32(0, m->sender);
                    stmt->setUInt32(1, itr2->item_guid);
                    CharacterDatabase.Execute(stmt);

                    stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_ITEM_OWNER);
                    stmt->setUInt32(0, m->sender);
                    stmt->setUInt32(1, itr2->item_guid);
                    CharacterDatabase.Execute(stmt);
                }
                delete m;
                continue;
            }
        }

        stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL);
        stmt->setUInt32(0, m->messageID);
        CharacterDatabase.Execute(stmt);
        delete m;
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u mails", count);
}

void ObjectMgr::LoadQuestAreaTriggers ()
{
    mQuestAreaTriggerMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT id, quest FROM areatrigger_involvedrelation");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u quest trigger points", count);
        return;
    }

    do
    {
        ++count;

        Field *fields = result->Fetch();

        uint32 trigger_ID = fields[0].GetUInt32();
        uint32 quest_ID = fields[1].GetUInt32();

        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(trigger_ID);
        if (!atEntry)
        {
            sLog->outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", trigger_ID);
            continue;
        }

        Quest const* quest = GetQuestTemplate(quest_ID);

        if (!quest)
        {
            sLog->outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not existing quest %u", trigger_ID, quest_ID);
            continue;
        }

        if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT))
        {
            sLog->outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);

            // this will prevent quest completing without objective
            const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT);

            // continue; - quest modified to required objective and trigger can be allowed.
        }

        mQuestAreaTriggerMap[trigger_ID] = quest_ID;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u quest trigger points", count);
}

void ObjectMgr::LoadAreaTriggerQuestStart ()
{
    mQuestStartAreaTriggerMap.clear();
    QueryResult result = WorldDatabase.Query("SELECT trigger_ID, quest_ID FROM areatrigger_queststart");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u queststart triggers", count);
        return;
    }

    do
    {
        ++count;
        Field *fields = result->Fetch();
        uint32 Trigger_ID = fields[0].GetUInt32();
        uint32 Quest_ID = fields[1].GetUInt32();

        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);

        if (!atEntry)
        {
            sLog->outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
            continue;
        }

        Quest const* quest = GetQuestTemplate(Quest_ID);

        if (!quest)
        {
            sLog->outErrorDb("Table `areatrigger_queststart` has record (id: %u) for not existing quest %u", Trigger_ID, Quest_ID);
            continue;
        }

        mQuestStartAreaTriggerMap[Trigger_ID] = Quest_ID;
    }
    while (result->NextRow());
    sLog->outString();
    sLog->outString(">> Loaded %u QuestGivers triggers", count);
}

void ObjectMgr::LoadTavernAreaTriggers ()
{
    mTavernAreaTriggerSet.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT id FROM areatrigger_tavern");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u tavern triggers", count);
        return;
    }

    do
    {
        ++count;

        Field *fields = result->Fetch();

        uint32 Trigger_ID = fields[0].GetUInt32();

        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
        if (!atEntry)
        {
            sLog->outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
            continue;
        }

        mTavernAreaTriggerSet.insert(Trigger_ID);
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u tavern triggers", count);
}

void ObjectMgr::LoadAreaTriggerScripts ()
{
    mAreaTriggerScripts.clear();          // need for reload case
    QueryResult result = WorldDatabase.Query("SELECT entry, ScriptName FROM areatrigger_scripts");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u areatrigger scripts", count);
        return;
    }

    do
    {
        ++count;

        Field *fields = result->Fetch();

        uint32 Trigger_ID = fields[0].GetUInt32();
        const char *scriptName = fields[1].GetCString();

        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
        if (!atEntry)
        {
            sLog->outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
            continue;
        }
        mAreaTriggerScripts[Trigger_ID] = GetScriptId(scriptName);
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u areatrigger scripts", count);
}

uint32 ObjectMgr::GetNearestTaxiNode (float x, float y, float z, uint32 mapid, uint32 team)
{
    bool found = false;
    float dist = 10000;
    uint32 id = 0;

    for (uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
    {
        TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);

        if (!node || node->map_id != mapid || (!node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981))          // dk flight
            continue;

        uint8 field = (uint8) ((i - 1) / 32);
        uint32 submask = 1 << ((i - 1) % 32);

        // skip not taxi network nodes
        if ((sTaxiNodesMask[field] & submask) == 0)
            continue;

        float dist2 = (node->x - x) * (node->x - x) + (node->y - y) * (node->y - y) + (node->z - z) * (node->z - z);
        if (found)
        {
            if (dist2 < dist)
            {
                dist = dist2;
                id = i;
            }
        }
        else
        {
            found = true;
            dist = dist2;
            id = i;
        }
    }

    return id;
}

void ObjectMgr::GetTaxiPath (uint32 source, uint32 destination, uint32 &path, uint32 &cost)
{
    TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
    if (src_i == sTaxiPathSetBySource.end())
    {
        path = 0;
        cost = 0;
        return;
    }

    TaxiPathSetForSource& pathSet = src_i->second;

    TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
    if (dest_i == pathSet.end())
    {
        path = 0;
        cost = 0;
        return;
    }

    cost = dest_i->second.price;
    path = dest_i->second.ID;
}

uint32 ObjectMgr::GetTaxiMountDisplayId (uint32 id, uint32 team, bool allowed_alt_team /* = false */)
{
    uint32 mount_id = 0;

    // select mount creature id
    TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
    if (node)
    {
        uint32 mount_entry = 0;
        if (team == ALLIANCE)
            mount_entry = node->MountCreatureID[1];
        else
            mount_entry = node->MountCreatureID[0];

        // Fix for Alliance not being able to use Acherus taxi
        // only one mount type for both sides
        if (mount_entry == 0 && allowed_alt_team)
        {
            // Simply reverse the selection. At least one team in theory should have a valid mount ID to choose.
            mount_entry = team == ALLIANCE ? node->MountCreatureID[0] : node->MountCreatureID[1];
        }

        CreatureInfo const *mount_info = GetCreatureTemplate(mount_entry);
        if (mount_info)
        {
            mount_id = mount_info->GetRandomValidModelId();
            if (!mount_id)
            {
                sLog->outErrorDb("No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry);
                return false;
            }
        }
    }

    CreatureModelInfo const *minfo = sObjectMgr->GetCreatureModelRandomGender(mount_id);
    if (minfo)
        mount_id = minfo->modelid;

    return mount_id;
}

void ObjectMgr::LoadGraveyardZones ()
{
    mGraveYardMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT id, ghost_zone, faction FROM game_graveyard_zone");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u graveyard-zone links", count);
        return;
    }

    do
    {
        ++count;

        Field *fields = result->Fetch();

        uint32 safeLocId = fields[0].GetUInt32();
        uint32 zoneId = fields[1].GetUInt32();
        uint32 team = fields[2].GetUInt32();

        WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId);
        if (!entry)
        {
            sLog->outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId);
            continue;
        }

        AreaTableEntry const *areaEntry = GetAreaEntryByAreaID(zoneId);
        if (!areaEntry)
        {
            sLog->outErrorDb("Table `game_graveyard_zone` has record for not existing zone id (%u), skipped.", zoneId);
            continue;
        }

        if (areaEntry->zone != 0)
        {
            sLog->outErrorDb("Table `game_graveyard_zone` has record subzone id (%u) instead of zone, skipped.", zoneId);
            continue;
        }

        if (team != 0 && team != HORDE && team != ALLIANCE)
        {
            sLog->outErrorDb("Table `game_graveyard_zone` has record for non player faction (%u), skipped.", team);
            continue;
        }

        if (!AddGraveYardLink(safeLocId, zoneId, team, false))
            sLog->outErrorDb("Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId);
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u graveyard-zone links", count);
}

WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard (float x, float y, float z, uint32 MapId, uint32 team)
{
    // search for zone associated closest graveyard
    uint32 zoneId = sMapMgr->GetZoneId(MapId, x, y, z);

    // Simulate std. algorithm:
    //   found some graveyard associated to (ghost_zone, ghost_map)
    //
    //   if mapId == graveyard.mapId (ghost in plain zone or city or battleground) and search graveyard at same map
    //     then check faction
    //   if mapId != graveyard.mapId (ghost in instance) and search any graveyard associated
    //     then check faction
    GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
    GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
    MapEntry const* map = sMapStore.LookupEntry(MapId);
    // not need to check validity of map object; MapId _MUST_ be valid here

    if (graveLow == graveUp && !map->IsBattleArena())
    {
        //sLog->outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
        return NULL;
    }

    // at corpse map
    bool foundNear = false;
    float distNear = 10000;
    WorldSafeLocsEntry const* entryNear = NULL;

    // at entrance map for corpse map
    bool foundEntr = false;
    float distEntr = 10000;
    WorldSafeLocsEntry const* entryEntr = NULL;

    // some where other
    WorldSafeLocsEntry const* entryFar = NULL;

    MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);

    for (GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
    {
        GraveYardData const& data = itr->second;

        WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId);
        if (!entry)
        {
            sLog->outErrorDb("Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId);
            continue;
        }

        // skip enemy faction graveyard
        // team == 0 case can be at call from .neargrave
        if (data.team != 0 && team != 0 && data.team != team)
            continue;

        // find now nearest graveyard at other map
        if (MapId != entry->map_id)
        {
            // if find graveyard at different map from where entrance placed (or no entrance data), use any first
            if (!mapEntry || mapEntry->entrance_map < 0 || uint32(mapEntry->entrance_map) != entry->map_id || (mapEntry->entrance_x == 0 && mapEntry->entrance_y == 0))
            {
                // not have any corrdinates for check distance anyway
                entryFar = entry;
                continue;
            }

            // at entrance map calculate distance (2D);
            float dist2 = (entry->x - mapEntry->entrance_x) * (entry->x - mapEntry->entrance_x) + (entry->y - mapEntry->entrance_y) * (entry->y - mapEntry->entrance_y);
            if (foundEntr)
            {
                if (dist2 < distEntr)
                {
                    distEntr = dist2;
                    entryEntr = entry;
                }
            }
            else
            {
                foundEntr = true;
                distEntr = dist2;
                entryEntr = entry;
            }
        }
        // find now nearest graveyard at same map
        else
        {
            float dist2 = (entry->x - x) * (entry->x - x) + (entry->y - y) * (entry->y - y) + (entry->z - z) * (entry->z - z);
            if (foundNear)
            {
                if (dist2 < distNear)
                {
                    distNear = dist2;
                    entryNear = entry;
                }
            }
            else
            {
                foundNear = true;
                distNear = dist2;
                entryNear = entry;
            }
        }
    }

    if (entryNear)
        return entryNear;

    if (entryEntr)
        return entryEntr;

    return entryFar;
}

GraveYardData const* ObjectMgr::FindGraveYardData (uint32 id, uint32 zoneId)
{
    GraveYardMap::const_iterator graveLow = mGraveYardMap.lower_bound(zoneId);
    GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);

    for (GraveYardMap::const_iterator itr = graveLow; itr != graveUp; ++itr)
    {
        if (itr->second.safeLocId == id)
            return &itr->second;
    }

    return NULL;
}

bool ObjectMgr::AddGraveYardLink (uint32 id, uint32 zoneId, uint32 team, bool inDB)
{
    if (FindGraveYardData(id, zoneId))
        return false;

    // add link to loaded data
    GraveYardData data;
    data.safeLocId = id;
    data.team = team;

    mGraveYardMap.insert(GraveYardMap::value_type(zoneId, data));

    // add link to DB
    if (inDB)
    {
        WorldDatabase.PExecute("INSERT INTO game_graveyard_zone (id, ghost_zone, faction) "
                "VALUES ('%u', '%u', '%u')", id, zoneId, team);
    }

    return true;
}

void ObjectMgr::RemoveGraveYardLink (uint32 id, uint32 zoneId, uint32 team, bool inDB)
{
    GraveYardMap::iterator graveLow = mGraveYardMap.lower_bound(zoneId);
    GraveYardMap::iterator graveUp = mGraveYardMap.upper_bound(zoneId);
    if (graveLow == graveUp)
    {
        //sLog->outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team);
        return;
    }

    bool found = false;

    GraveYardMap::iterator itr;

    for (itr = graveLow; itr != graveUp; ++itr)
    {
        GraveYardData & data = itr->second;

        // skip not matching safezone id
        if (data.safeLocId != id)
            continue;

        // skip enemy faction graveyard at same map (normal area, city, or battleground)
        // team == 0 case can be at call from .neargrave
        if (data.team != 0 && team != 0 && data.team != team)
            continue;

        found = true;
        break;
    }

    // no match, return
    if (!found)
        return;

    // remove from links
    mGraveYardMap.erase(itr);

    // remove link from DB
    if (inDB)
    {
        WorldDatabase.PExecute("DELETE FROM game_graveyard_zone WHERE id = '%u' AND ghost_zone = '%u' AND faction = '%u'", id, zoneId, team);
    }

    return;
}

void ObjectMgr::LoadAreaTriggerTeleports ()
{
    mAreaTriggers.clear();          // need for reload case

    uint32 count = 0;

    //                                                        0            1                  2                  3                  4                   5
    QueryResult result = WorldDatabase.Query("SELECT id,  target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport");
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u area trigger teleport definitions", count);
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        ++count;

        uint32 Trigger_ID = fields[0].GetUInt32();

        AreaTrigger at;

        at.target_mapId = fields[1].GetUInt32();
        at.target_X = fields[2].GetFloat();
        at.target_Y = fields[3].GetFloat();
        at.target_Z = fields[4].GetFloat();
        at.target_Orientation = fields[5].GetFloat();

        AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
        if (!atEntry)
        {
            sLog->outErrorDb("Area trigger (ID:%u) does not exist in `AreaTrigger.dbc`.", Trigger_ID);
            continue;
        }

        MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId);
        if (!mapEntry)
        {
            sLog->outErrorDb("Area trigger (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.", Trigger_ID, at.target_mapId);
            continue;
        }

        if (at.target_X == 0 && at.target_Y == 0 && at.target_Z == 0)
        {
            sLog->outErrorDb("Area trigger (ID:%u) target coordinates not provided.", Trigger_ID);
            continue;
        }

        mAreaTriggers[Trigger_ID] = at;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u area trigger teleport definitions", count);
}

void ObjectMgr::LoadAccessRequirements ()
{
    mAccessRequirements.clear();          // need for reload case

    uint32 count = 0;

    //                                               0           1          2          3     4      5             6             7                      8                  9
    QueryResult result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item, item2, quest_done_A, quest_done_H, completed_achievement, quest_failed_text FROM access_requirement");
    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u access requirement definitions", count);
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        ++count;

        uint32 mapid = fields[0].GetUInt32();
        uint8 difficulty = fields[1].GetUInt8();
        uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);

        AccessRequirement ar;

        ar.levelMin = fields[2].GetUInt8();
        ar.levelMax = fields[3].GetUInt8();
        ar.item = fields[4].GetUInt32();
        ar.item2 = fields[5].GetUInt32();
        ar.quest_A = fields[6].GetUInt32();
        ar.quest_H = fields[7].GetUInt32();
        ar.achievement = fields[8].GetUInt32();
        ar.questFailedText = fields[9].GetString();

        if (ar.item)
        {
            ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(ar.item);
            if (!pProto)
            {
                sLog->outError("Key item %u does not exist for map %u difficulty %u, removing key requirement.", ar.item, mapid, difficulty);
                ar.item = 0;
            }
        }

        if (ar.item2)
        {
            ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(ar.item2);
            if (!pProto)
            {
                sLog->outError("Second item %u does not exist for map %u difficulty %u, removing key requirement.", ar.item2, mapid, difficulty);
                ar.item2 = 0;
            }
        }

        if (ar.quest_A)
        {
            if (!GetQuestTemplate(ar.quest_A))
            {
                sLog->outErrorDb("Required Alliance Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar.quest_A, mapid, difficulty);
                ar.quest_A = 0;
            }
        }

        if (ar.quest_H)
        {
            if (!GetQuestTemplate(ar.quest_H))
            {
                sLog->outErrorDb("Required Horde Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar.quest_H, mapid, difficulty);
                ar.quest_H = 0;
            }
        }

        if (ar.achievement)
        {
            if (!sAchievementStore.LookupEntry(ar.achievement))
            {
                sLog->outErrorDb("Required Achievement %u not exist for map %u difficulty %u, remove quest done requirement.", ar.achievement, mapid, difficulty);
                ar.achievement = 0;
            }
        }

        mAccessRequirements[requirement_ID] = ar;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u access requirement definitions", count);
}

/*
 * Searches for the areatrigger which teleports players out of the given map with instance_template.parent field support
 */
AreaTrigger const* ObjectMgr::GetGoBackTrigger (uint32 Map) const
{
    bool useParentDbValue = false;
    uint32 parentId = 0;
    const MapEntry *mapEntry = sMapStore.LookupEntry(Map);
    if (!mapEntry || mapEntry->entrance_map < 0)
        return NULL;

    if (mapEntry->IsDungeon())
    {
        const InstanceTemplate *iTemplate = ObjectMgr::GetInstanceTemplate(Map);

        if (!iTemplate)
            return NULL;

        parentId = iTemplate->parent;
        useParentDbValue = true;
    }

    uint32 entrance_map = uint32(mapEntry->entrance_map);
    for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
        if ((!useParentDbValue && itr->second.target_mapId == entrance_map) || (useParentDbValue && itr->second.target_mapId == parentId))
        {
            AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
            if (atEntry && atEntry->mapid == Map)
                return &itr->second;
        }
    return NULL;
}

/**
 * Searches for the areatrigger which teleports players to the given map
 */
AreaTrigger const* ObjectMgr::GetMapEntranceTrigger (uint32 Map) const
{
    for (AreaTriggerMap::const_iterator itr = mAreaTriggers.begin(); itr != mAreaTriggers.end(); ++itr)
    {
        if (itr->second.target_mapId == Map)
        {
            AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first);
            if (atEntry)
                return &itr->second;
        }
    }
    return NULL;
}

void ObjectMgr::SetHighestGuids ()
{
    QueryResult result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
    if (result)
        m_hiCharGuid = (*result)[0].GetUInt32() + 1;

    result = WorldDatabase.Query("SELECT MAX(guid) FROM creature");
    if (result)
        m_hiCreatureGuid = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(guid) FROM item_instance");
    if (result)
        m_hiItemGuid = (*result)[0].GetUInt32() + 1;

    // Cleanup other tables from not existed guids ( >= m_hiItemGuid)
    CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item >= '%u'", m_hiItemGuid);
    CharacterDatabase.PExecute("DELETE FROM mail_items WHERE item_guid >= '%u'", m_hiItemGuid);
    CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE itemguid >= '%u'", m_hiItemGuid);
    CharacterDatabase.PExecute("DELETE FROM guild_bank_item WHERE item_guid >= '%u'", m_hiItemGuid);

    result = WorldDatabase.Query("SELECT MAX(guid) FROM gameobject");
    if (result)
        m_hiGoGuid = (*result)[0].GetUInt32() + 1;

    result = WorldDatabase.Query("SELECT MAX(guid) FROM transports");
    if (result)
        m_hiMoTransGuid = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(id) FROM auctionhouse");
    if (result)
        m_auctionid = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(id) FROM mail");
    if (result)
        m_mailid = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(corpseGuid) FROM corpse");
    if (result)
        m_hiCorpseGuid = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
    if (result)
        m_arenaTeamId = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
    if (result)
        m_equipmentSetGuid = (*result)[0].GetUInt64() + 1;

    result = CharacterDatabase.Query("SELECT MAX(guildid) FROM guild");
    if (result)
        m_guildId = (*result)[0].GetUInt32() + 1;

    result = CharacterDatabase.Query("SELECT MAX(guid) FROM groups");
    if (result)
        m_hiGroupGuid = (*result)[0].GetUInt32() + 1;
}

uint32 ObjectMgr::GenerateArenaTeamId ()
{
    if (m_arenaTeamId >= 0xFFFFFFFE)
    {
        sLog->outError("Arena team ids overflow!! Can't continue, shutting down server. ");
        World::StopNow(ERROR_EXIT_CODE);
    }
    return m_arenaTeamId++;
}

uint32 ObjectMgr::GenerateAuctionID ()
{
    if (m_auctionid >= 0xFFFFFFFE)
    {
        sLog->outError("Auctions ids overflow!! Can't continue, shutting down server. ");
        World::StopNow(ERROR_EXIT_CODE);
    }
    return m_auctionid++;
}

uint64 ObjectMgr::GenerateEquipmentSetGuid ()
{
    if (m_equipmentSetGuid >= 0xFFFFFFFFFFFFFFFEll)
    {
        sLog->outError("EquipmentSet guid overflow!! Can't continue, shutting down server. ");
        World::StopNow(ERROR_EXIT_CODE);
    }
    return m_equipmentSetGuid++;
}

uint32 ObjectMgr::GenerateGuildId ()
{
    if (m_guildId >= 0xFFFFFFFE)
    {
        sLog->outError("Guild ids overflow!! Can't continue, shutting down server. ");
        World::StopNow(ERROR_EXIT_CODE);
    }
    return m_guildId++;
}

uint32 ObjectMgr::GenerateMailID ()
{
    if (m_mailid >= 0xFFFFFFFE)
    {
        sLog->outError("Mail ids overflow!! Can't continue, shutting down server. ");
        World::StopNow(ERROR_EXIT_CODE);
    }
    return m_mailid++;
}

uint32 ObjectMgr::GenerateLowGuid (HighGuid guidhigh)
{
    switch (guidhigh)
    {
    case HIGHGUID_ITEM:
        if (m_hiItemGuid >= 0xFFFFFFFE)
        {
            sLog->outError("Item guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiItemGuid++;
    case HIGHGUID_UNIT:
        if (m_hiCreatureGuid >= 0x00FFFFFE)
        {
            sLog->outError("Creature guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiCreatureGuid++;
    case HIGHGUID_PET:
        if (m_hiPetGuid >= 0x00FFFFFE)
        {
            sLog->outError("Pet guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiPetGuid++;
    case HIGHGUID_VEHICLE:
        if (m_hiVehicleGuid >= 0x00FFFFFF)
        {
            sLog->outError("Vehicle guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiVehicleGuid++;
    case HIGHGUID_PLAYER:
        if (m_hiCharGuid >= 0xFFFFFFFE)
        {
            sLog->outError("Players guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiCharGuid++;
    case HIGHGUID_GAMEOBJECT:
        if (m_hiGoGuid >= 0x00FFFFFE)
        {
            sLog->outError("Gameobject guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiGoGuid++;
    case HIGHGUID_CORPSE:
        if (m_hiCorpseGuid >= 0xFFFFFFFE)
        {
            sLog->outError("Corpse guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiCorpseGuid++;
    case HIGHGUID_DYNAMICOBJECT:
        if (m_hiDoGuid >= 0xFFFFFFFE)
        {
            sLog->outError("DynamicObject guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiDoGuid++;
    case HIGHGUID_GROUP:
        if (m_hiGroupGuid >= 0xFFFFFFFE)
        {
            sLog->outError("Group guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiGroupGuid++;
    case HIGHGUID_MO_TRANSPORT:
        if (m_hiMoTransGuid >= 0xFFFFFFFE)
        {
            sLog->outError("MO Transport guid overflow!! Can't continue, shutting down server. ");
            World::StopNow(ERROR_EXIT_CODE);
        }
        return m_hiMoTransGuid++;
    default:
        ASSERT(0)
        ;
    }

    ASSERT(0);
    return 0;
}

void ObjectMgr::LoadGameObjectLocales ()
{
    mGameObjectLocaleMap.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT entry, "
            "name_loc1, name_loc2, name_loc3, name_loc4, name_loc5, name_loc6, name_loc7, name_loc8, "
            "castbarcaption_loc1, castbarcaption_loc2, castbarcaption_loc3, castbarcaption_loc4, "
            "castbarcaption_loc5, castbarcaption_loc6, castbarcaption_loc7, castbarcaption_loc8 FROM locales_gameobject");

    if (!result)
        return;

    do
    {
        Field *fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();

        GameObjectLocale& data = mGameObjectLocaleMap[entry];

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            std::string str = fields[i].GetString();
            AddLocaleString(str, LocaleConstant(i), data.Name);
        }

        for (uint8 i = 1; i < TOTAL_LOCALES; ++i)
        {
            std::string str = fields[i + (TOTAL_LOCALES - 1)].GetString();
            AddLocaleString(str, LocaleConstant(i), data.CastBarCaption);
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %lu gameobject locale strings", (unsigned long) mGameObjectLocaleMap.size());
}

struct SQLGameObjectLoader: public SQLStorageLoaderBase<SQLGameObjectLoader>
{
    template<class D>
    void convert_from_str (uint32 /*field_pos*/, char *src, D &dst)
    {
        dst = D(sObjectMgr->GetScriptId(src));
    }
};

inline void CheckGOLockId (GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
{
    if (sLockStore.LookupEntry(dataN))
        return;

    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.", goInfo->id, goInfo->type, N, goInfo->door.lockId, goInfo->door.lockId);
}

inline void CheckGOLinkedTrapId (GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
{
    if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN))
    {
        if (trapInfo->type != GAMEOBJECT_TYPE_TRAP)
            sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but GO (Entry %u) have not GAMEOBJECT_TYPE_TRAP (%u) type.", goInfo->id, goInfo->type, N, dataN, dataN, GAMEOBJECT_TYPE_TRAP);
    }
}

inline void CheckGOSpellId (GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
{
    if (sSpellStore.LookupEntry(dataN))
        return;

    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.", goInfo->id, goInfo->type, N, dataN, dataN);
}

inline void CheckAndFixGOChairHeightId (GameObjectInfo const* goInfo, uint32 const& dataN, uint32 N)
{
    if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR))
        return;

    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.", goInfo->id, goInfo->type, N, dataN, UNIT_STAND_STATE_SIT_HIGH_CHAIR - UNIT_STAND_STATE_SIT_LOW_CHAIR);

    // prevent client and server unexpected work
    const_cast<uint32&>(dataN) = 0;
}

inline void CheckGONoDamageImmuneId (GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
{
    // 0/1 correct values
    if (dataN <= 1)
        return;

    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.", goInfo->id, goInfo->type, N, dataN);
}

inline void CheckGOConsumable (GameObjectInfo const* goInfo, uint32 dataN, uint32 N)
{
    // 0/1 correct values
    if (dataN <= 1)
        return;

    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.", goInfo->id, goInfo->type, N, dataN);
}

void ObjectMgr::LoadGameobjectInfo ()
{
    SQLGameObjectLoader loader;
    loader.Load(sGOStorage);

    // some checks
    for (uint32 id = 1; id < sGOStorage.MaxEntry; id++)
    {
        GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
        if (!goInfo)
            continue;

        // some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore

        switch (goInfo->type)
        {
        case GAMEOBJECT_TYPE_DOOR:          //0
        {
            if (goInfo->door.lockId)
                CheckGOLockId(goInfo, goInfo->door.lockId, 1);
            CheckGONoDamageImmuneId(goInfo, goInfo->door.noDamageImmune, 3);
            break;
        }
        case GAMEOBJECT_TYPE_BUTTON:          //1
        {
            if (goInfo->button.lockId)
                CheckGOLockId(goInfo, goInfo->button.lockId, 1);
            CheckGONoDamageImmuneId(goInfo, goInfo->button.noDamageImmune, 4);
            break;
        }
        case GAMEOBJECT_TYPE_QUESTGIVER:          //2
        {
            if (goInfo->questgiver.lockId)
                CheckGOLockId(goInfo, goInfo->questgiver.lockId, 0);
            CheckGONoDamageImmuneId(goInfo, goInfo->questgiver.noDamageImmune, 5);
            break;
        }
        case GAMEOBJECT_TYPE_CHEST:          //3
        {
            if (goInfo->chest.lockId)
                CheckGOLockId(goInfo, goInfo->chest.lockId, 0);

            CheckGOConsumable(goInfo, goInfo->chest.consumable, 3);

            if (goInfo->chest.linkedTrapId)          // linked trap
                CheckGOLinkedTrapId(goInfo, goInfo->chest.linkedTrapId, 7);
            break;
        }
        case GAMEOBJECT_TYPE_TRAP:          //6
        {
            if (goInfo->trap.lockId)
                CheckGOLockId(goInfo, goInfo->trap.lockId, 0);
            break;
        }
        case GAMEOBJECT_TYPE_CHAIR:          //7
            CheckAndFixGOChairHeightId(goInfo, goInfo->chair.height, 1);
            break;
        case GAMEOBJECT_TYPE_SPELL_FOCUS:          //8
        {
            if (goInfo->spellFocus.focusId)
            {
                if (!sSpellFocusObjectStore.LookupEntry(goInfo->spellFocus.focusId))
                    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but SpellFocus (Id: %u) not exist.", id, goInfo->type, goInfo->spellFocus.focusId, goInfo->spellFocus.focusId);
            }

            if (goInfo->spellFocus.linkedTrapId)          // linked trap
                CheckGOLinkedTrapId(goInfo, goInfo->spellFocus.linkedTrapId, 2);
            break;
        }
        case GAMEOBJECT_TYPE_GOOBER:          //10
        {
            if (goInfo->goober.lockId)
                CheckGOLockId(goInfo, goInfo->goober.lockId, 0);

            CheckGOConsumable(goInfo, goInfo->goober.consumable, 3);

            if (goInfo->goober.pageId)          // pageId
            {
                if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
                    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data7=%u but PageText (Entry %u) not exist.", id, goInfo->type, goInfo->goober.pageId, goInfo->goober.pageId);
            }
            CheckGONoDamageImmuneId(goInfo, goInfo->goober.noDamageImmune, 11);
            if (goInfo->goober.linkedTrapId)          // linked trap
                CheckGOLinkedTrapId(goInfo, goInfo->goober.linkedTrapId, 12);
            break;
        }
        case GAMEOBJECT_TYPE_AREADAMAGE:          //12
        {
            if (goInfo->areadamage.lockId)
                CheckGOLockId(goInfo, goInfo->areadamage.lockId, 0);
            break;
        }
        case GAMEOBJECT_TYPE_CAMERA:          //13
        {
            if (goInfo->camera.lockId)
                CheckGOLockId(goInfo, goInfo->camera.lockId, 0);
            break;
        }
        case GAMEOBJECT_TYPE_MO_TRANSPORT:          //15
        {
            if (goInfo->moTransport.taxiPathId)
            {
                if (goInfo->moTransport.taxiPathId >= sTaxiPathNodesByPath.size() || sTaxiPathNodesByPath[goInfo->moTransport.taxiPathId].empty())
                    sLog->outErrorDb("Gameobject (Entry: %u GoType: %u) have data0=%u but TaxiPath (Id: %u) not exist.", id, goInfo->type, goInfo->moTransport.taxiPathId, goInfo->moTransport.taxiPathId);
            }
            break;
        }
        case GAMEOBJECT_TYPE_SUMMONING_RITUAL:          //18
            break;
        case GAMEOBJECT_TYPE_SPELLCASTER:          //22
        {
            // always must have spell
            CheckGOSpellId(goInfo, goInfo->spellcaster.spellId, 0);
            break;
        }
        case GAMEOBJECT_TYPE_FLAGSTAND:          //24
        {
            if (goInfo->flagstand.lockId)
                CheckGOLockId(goInfo, goInfo->flagstand.lockId, 0);
            CheckGONoDamageImmuneId(goInfo, goInfo->flagstand.noDamageImmune, 5);
            break;
        }
        case GAMEOBJECT_TYPE_FISHINGHOLE:          //25
        {
            if (goInfo->fishinghole.lockId)
                CheckGOLockId(goInfo, goInfo->fishinghole.lockId, 4);
            break;
        }
        case GAMEOBJECT_TYPE_FLAGDROP:          //26
        {
            if (goInfo->flagdrop.lockId)
                CheckGOLockId(goInfo, goInfo->flagdrop.lockId, 0);
            CheckGONoDamageImmuneId(goInfo, goInfo->flagdrop.noDamageImmune, 3);
            break;
        }
        case GAMEOBJECT_TYPE_BARBER_CHAIR:          //32
            CheckAndFixGOChairHeightId(goInfo, goInfo->barberChair.chairheight, 0);
            break;
        }
    }

    sLog->outString(">> Loaded %u game object templates", sGOStorage.RecordCount);
    sLog->outString();
}

void ObjectMgr::LoadExplorationBaseXP ()
{
    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT level, basexp FROM exploration_basexp");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u BaseXP definitions", count);
        return;
    }

    do
    {
        Field *fields = result->Fetch();
        uint8 level = fields[0].GetUInt8();
        uint32 basexp = fields[1].GetUInt32();
        mBaseXPTable[level] = basexp;
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u BaseXP definitions", count);
}

uint32 ObjectMgr::GetBaseXP (uint8 level)
{
    return mBaseXPTable[level] ? mBaseXPTable[level] : 0;
}

uint32 ObjectMgr::GetXPForLevel (uint8 level)
{
    if (level < mPlayerXPperLevel.size())
        return mPlayerXPperLevel[level];
    return 0;
}

uint32 ObjectMgr::GetXPForGuildLevel (uint8 level)
{
    if (level < mGuildXPperLevel.size())
        return mGuildXPperLevel[level];
    return 0;
}

void ObjectMgr::LoadPetNames ()
{
    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT word, entry, half FROM pet_name_generation");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u pet name parts", count);
        return;
    }

    do
    {
        Field *fields = result->Fetch();
        std::string word = fields[0].GetString();
        uint32 entry = fields[1].GetUInt32();
        bool half = fields[2].GetBool();
        if (half)
            PetHalfName1[entry].push_back(word);
        else
            PetHalfName0[entry].push_back(word);
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u pet name parts", count);
}

void ObjectMgr::LoadPetNumber ()
{
    QueryResult result = CharacterDatabase.Query("SELECT MAX(id) FROM character_pet");
    if (result)
    {
        Field *fields = result->Fetch();
        m_hiPetNumber = fields[0].GetUInt32() + 1;
    }

    sLog->outString();
    sLog->outString(">> Loaded the max pet number: %d", m_hiPetNumber - 1);
}

std::string ObjectMgr::GeneratePetName (uint32 entry)
{
    StringVector & list0 = PetHalfName0[entry];
    StringVector & list1 = PetHalfName1[entry];

    if (list0.empty() || list1.empty())
    {
        CreatureInfo const *cinfo = GetCreatureTemplate(entry);
        const char* petname = GetPetName(cinfo->family);
        if (!petname)
            petname = cinfo->Name;
        return std::string(petname);
    }

    return *(list0.begin() + urand(0, list0.size() - 1)) + *(list1.begin() + urand(0, list1.size() - 1));
}

uint32 ObjectMgr::GeneratePetNumber ()
{
    return ++m_hiPetNumber;
}

void ObjectMgr::LoadCorpses ()
{
    uint32 oldMSTime = getMSTime();

    PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_LOAD_CORPSES));
    if (!result)
    {
        sLog->outString(">> Loaded 0 corpses. DB table `pet_name_generation` is empty.");
        sLog->outString();
        return;
    }

    uint32 count = 0;
    do
    {
        Field *fields = result->Fetch();
        uint32 guid = fields[0].GetUInt32();

        Corpse *corpse = new Corpse();
        if (!corpse->LoadFromDB(guid, fields))
        {
            delete corpse;
            continue;
        }

        sObjectAccessor->AddCorpse(corpse);
        ++count;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded %u corpses in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

void ObjectMgr::LoadReputationRewardRate ()
{
    m_RepRewardRateMap.clear();          // for reload case

    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT faction, quest_rate, creature_rate, spell_rate FROM reputation_reward_rate");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `reputation_reward_rate`, table is empty!");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 factionId = fields[0].GetUInt32();

        RepRewardRate repRate;

        repRate.quest_rate = fields[1].GetFloat();
        repRate.creature_rate = fields[2].GetFloat();
        repRate.spell_rate = fields[3].GetFloat();

        FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);
        if (!factionEntry)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_reward_rate`", factionId);
            continue;
        }

        if (repRate.quest_rate < 0.0f)
        {
            sLog->outErrorDb("Table reputation_reward_rate has quest_rate with invalid rate %f, skipping data for faction %u", repRate.quest_rate, factionId);
            continue;
        }

        if (repRate.creature_rate < 0.0f)
        {
            sLog->outErrorDb("Table reputation_reward_rate has creature_rate with invalid rate %f, skipping data for faction %u", repRate.creature_rate, factionId);
            continue;
        }

        if (repRate.spell_rate < 0.0f)
        {
            sLog->outErrorDb("Table reputation_reward_rate has spell_rate with invalid rate %f, skipping data for faction %u", repRate.spell_rate, factionId);
            continue;
        }

        m_RepRewardRateMap[factionId] = repRate;

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u reputation_reward_rate", count);
}

void ObjectMgr::LoadRewardOnKill ()
{
    uint32 oldMSTime = getMSTime();

    // For reload case
    mRewOnKill.clear();

    uint32 count = 0;

    //                                                0            1                     2
    QueryResult result = WorldDatabase.Query("SELECT creature_id, RewOnKillRepFaction1, RewOnKillRepFaction2, "
    //   3             4             5                   6             7             8                   9
            "IsTeamAward1, MaxStanding1, RewOnKillRepValue1, IsTeamAward2, MaxStanding2, RewOnKillRepValue2, TeamDependent, "
    //   10            11            12                  13              14              15
            "CurrencyId1,  CurrencyId2,  CurrencyId3,        CurrencyCount1, CurrencyCount2, CurrencyCount3 "
            "FROM creature_onkill_reward");

    if (!result)
    {
        sLog->outErrorDb(">> Loaded 0 creature reward definitions. DB table `creature_onkill_reward` is empty.");
        sLog->outString();
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 creature_id = fields[0].GetUInt32();

        RewardOnKillEntry rewOnKill;
        rewOnKill.repfaction1 = fields[1].GetUInt32();
        rewOnKill.repfaction2 = fields[2].GetUInt32();
        rewOnKill.is_teamaward1 = fields[3].GetBool();
        rewOnKill.reputation_max_cap1 = fields[4].GetUInt32();
        rewOnKill.repvalue1 = fields[5].GetInt32();
        rewOnKill.is_teamaward2 = fields[6].GetBool();
        rewOnKill.reputation_max_cap2 = fields[7].GetUInt32();
        rewOnKill.repvalue2 = fields[8].GetInt32();
        rewOnKill.team_dependent = fields[9].GetUInt8();
        rewOnKill.currencyid1 = fields[10].GetUInt32();
        rewOnKill.currencyid2 = fields[11].GetUInt32();
        rewOnKill.currencyid3 = fields[12].GetUInt32();
        rewOnKill.currencycount1 = fields[13].GetInt32();
        rewOnKill.currencycount2 = fields[14].GetInt32();
        rewOnKill.currencycount3 = fields[15].GetInt32();

        if (!GetCreatureTemplate(creature_id))
        {
            sLog->outErrorDb("Table `creature_onkill_reward` have data for not existed creature entry (%u), skipped", creature_id);
            continue;
        }

        if (rewOnKill.repfaction1)
        {
            FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(rewOnKill.repfaction1);
            if (!factionEntry1)
            {
                sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reward`", rewOnKill.repfaction1);
                continue;
            }
        }

        if (rewOnKill.repfaction2)
        {
            FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(rewOnKill.repfaction2);
            if (!factionEntry2)
            {
                sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `creature_onkill_reward`", rewOnKill.repfaction2);
                continue;
            }
        }

        if (rewOnKill.currencyid1)
        {
            if (!sCurrencyTypesStore.LookupEntry(rewOnKill.currencyid1))
            {
                sLog->outErrorDb("CurrencyType (CurrencyTypes.dbc) %u does not exist but is used in `creature_onkill_reward`", rewOnKill.currencyid1);
                continue;
            }
        }

        if (rewOnKill.currencyid2)
        {
            if (!sCurrencyTypesStore.LookupEntry(rewOnKill.currencyid2))
            {
                sLog->outErrorDb("CurrencyType (CurrencyTypes.dbc) %u does not exist but is used in `creature_onkill_reward`", rewOnKill.currencyid2);
                continue;
            }
        }

        if (rewOnKill.currencyid3)
        {
            if (!sCurrencyTypesStore.LookupEntry(rewOnKill.currencyid3))
            {
                sLog->outErrorDb("CurrencyType (CurrencyTypes.dbc) %u does not exist but is used in `creature_onkill_reward`", rewOnKill.currencyid3);
                continue;
            }
        }

        mRewOnKill[creature_id] = rewOnKill;

        ++count;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded %u creature reward definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

void ObjectMgr::LoadReputationSpilloverTemplate ()
{
    uint32 oldMSTime = getMSTime();

    m_RepSpilloverTemplateMap.clear();          // for reload case

    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT faction, faction1, rate_1, rank_1, faction2, rate_2, rank_2, faction3, rate_3, rank_3, faction4, rate_4, rank_4 FROM reputation_spillover_template");

    if (!result)
    {
        sLog->outString(">> Loaded `reputation_spillover_template`, table is empty.");
        sLog->outString();
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 factionId = fields[0].GetUInt32();

        RepSpilloverTemplate repTemplate;

        repTemplate.faction[0] = fields[1].GetUInt32();
        repTemplate.faction_rate[0] = fields[2].GetFloat();
        repTemplate.faction_rank[0] = fields[3].GetUInt32();
        repTemplate.faction[1] = fields[4].GetUInt32();
        repTemplate.faction_rate[1] = fields[5].GetFloat();
        repTemplate.faction_rank[1] = fields[6].GetUInt32();
        repTemplate.faction[2] = fields[7].GetUInt32();
        repTemplate.faction_rate[2] = fields[8].GetFloat();
        repTemplate.faction_rank[2] = fields[9].GetUInt32();
        repTemplate.faction[3] = fields[10].GetUInt32();
        repTemplate.faction_rate[3] = fields[11].GetFloat();
        repTemplate.faction_rank[3] = fields[12].GetUInt32();

        FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionId);

        if (!factionEntry)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", factionId);
            continue;
        }

        if (factionEntry->team == 0)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u in `reputation_spillover_template` does not belong to any team, skipping", factionId);
            continue;
        }

        for (uint32 i = 0; i < MAX_SPILLOVER_FACTIONS; ++i)
        {
            if (repTemplate.faction[i])
            {
                FactionEntry const *factionSpillover = sFactionStore.LookupEntry(repTemplate.faction[i]);

                if (!factionSpillover)
                {
                    sLog->outErrorDb("Spillover faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template` for faction %u, skipping", repTemplate.faction[i], factionId);
                    continue;
                }

                if (factionSpillover->reputationListID < 0)
                {
                    sLog->outErrorDb("Spillover faction (faction.dbc) %u for faction %u in `reputation_spillover_template` can not be listed for client, and then useless, skipping", repTemplate.faction[i], factionId);
                    continue;
                }

                if (repTemplate.faction_rank[i] >= MAX_REPUTATION_RANK)
                {
                    sLog->outErrorDb("Rank %u used in `reputation_spillover_template` for spillover faction %u is not valid, skipping", repTemplate.faction_rank[i], repTemplate.faction[i]);
                    continue;
                }
            }
        }

        FactionEntry const *factionEntry0 = sFactionStore.LookupEntry(repTemplate.faction[0]);
        if (repTemplate.faction[0] && !factionEntry0)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[0]);
            continue;
        }
        FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repTemplate.faction[1]);
        if (repTemplate.faction[1] && !factionEntry1)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[1]);
            continue;
        }
        FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repTemplate.faction[2]);
        if (repTemplate.faction[2] && !factionEntry2)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[2]);
            continue;
        }
        FactionEntry const *factionEntry3 = sFactionStore.LookupEntry(repTemplate.faction[3]);
        if (repTemplate.faction[3] && !factionEntry3)
        {
            sLog->outErrorDb("Faction (faction.dbc) %u does not exist but is used in `reputation_spillover_template`", repTemplate.faction[3]);
            continue;
        }

        m_RepSpilloverTemplateMap[factionId] = repTemplate;

        ++count;
    }
    while (result->NextRow());

    sLog->outString(">> Loaded %u reputation_spillover_template in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
    sLog->outString();
}

void ObjectMgr::LoadPointsOfInterest ()
{
    mPointsOfInterest.clear();          // need for reload case

    uint32 count = 0;

    //                                                0      1  2  3      4     5     6
    QueryResult result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 point_id = fields[0].GetUInt16();

        PointOfInterest POI;
        POI.x = fields[1].GetFloat();
        POI.y = fields[2].GetFloat();
        POI.icon = fields[3].GetUInt16();
        POI.flags = fields[4].GetUInt16();
        POI.data = fields[5].GetUInt16();
        POI.icon_name = fields[6].GetString();

        if (!Trinity::IsValidMapCoord(POI.x, POI.y))
        {
            sLog->outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.", point_id, POI.x, POI.y);
            continue;
        }

        mPointsOfInterest[point_id] = POI;

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u Points of Interest definitions", count);
}

void ObjectMgr::LoadQuestPOI ()
{
    mQuestPOIMap.clear();          // need for reload case

    uint32 count = 0;

    //                                               0        1   2         3      4               5        6     7
    QueryResult result = WorldDatabase.Query("SELECT questId, id, objIndex, mapid, WorldMapAreaId, FloorId, unk3, unk4 FROM quest_poi order by questId");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 quest POI definitions. DB table `quest_poi` is empty.");
        return;
    }

    //                                                0        1   2  3
    QueryResult points = WorldDatabase.PQuery("SELECT questId, id, x, y FROM quest_poi_points ORDER BY questId DESC, idx");

    std::vector < std::vector<std::vector<QuestPOIPoint> > > POIs;

    if (points)
    {
        // The first result should have the highest questId
        Field *fields = points->Fetch();
        uint32 questId = fields[0].GetUInt32();
        POIs.resize(questId + 1);

        do
        {
            Field *fields = points->Fetch();

            uint32 questId = fields[0].GetUInt32();
            uint32 id = fields[1].GetUInt32();
            int32 x = fields[2].GetInt32();
            int32 y = fields[3].GetInt32();

            if (POIs[questId].size() <= id + 1)
                POIs[questId].resize(id + 10);

            QuestPOIPoint point(x, y);
            POIs[questId][id].push_back(point);
        }
        while (points->NextRow());
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 questId = fields[0].GetUInt32();
        uint32 id = fields[1].GetUInt32();
        int32 objIndex = fields[2].GetInt32();
        uint32 mapId = fields[3].GetUInt32();
        uint32 WorldMapAreaId = fields[4].GetUInt32();
        uint32 FloorId = fields[5].GetUInt32();
        uint32 unk3 = fields[6].GetUInt32();
        uint32 unk4 = fields[7].GetUInt32();

        QuestPOI POI(id, objIndex, mapId, WorldMapAreaId, FloorId, unk3, unk4);
        POI.points = POIs[questId][id];

        mQuestPOIMap[questId].push_back(POI);

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u quest POI definitions", count);
}

void ObjectMgr::LoadNPCSpellClickSpells ()
{
    uint32 count = 0;

    mSpellClickInfoMap.clear();
    //                                                0          1         2            3                   4          5           6              7               8
    QueryResult result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags, aura_required, aura_forbidden, user_type FROM npc_spellclick_spells");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 npc_entry = fields[0].GetUInt32();
        CreatureInfo const* cInfo = GetCreatureTemplate(npc_entry);
        if (!cInfo)
        {
            sLog->outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
            continue;
        }

        if (!(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK))
            const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK;

        uint32 spellid = fields[1].GetUInt32();
        SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid);
        if (!spellinfo)
        {
            sLog->outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid);
            continue;
        }

        uint32 auraRequired = fields[6].GetUInt32();
        if (auraRequired)
        {
            SpellEntry const *aurReqInfo = sSpellStore.LookupEntry(auraRequired);
            if (!aurReqInfo)
            {
                sLog->outErrorDb("Table npc_spellclick_spells references unknown aura required %u. Skipping entry.", auraRequired);
                continue;
            }
        }

        uint32 auraForbidden = fields[7].GetUInt32();
        if (auraForbidden)
        {
            SpellEntry const *aurForInfo = sSpellStore.LookupEntry(auraForbidden);
            if (!aurForInfo)
            {
                sLog->outErrorDb("Table npc_spellclick_spells references unknown aura forbidden %u. Skipping entry.", auraForbidden);
                continue;
            }
        }

        uint32 quest_start = fields[2].GetUInt32();

        // quest might be 0 to enable spellclick independent of any quest
        if (quest_start)
        {
            if (mQuestTemplates.find(quest_start) == mQuestTemplates.end())
            {
                sLog->outErrorDb("Table npc_spellclick_spells references unknown start quest %u. Skipping entry.", quest_start);
                continue;
            }
        }

        bool quest_start_active = fields[3].GetBool();

        uint32 quest_end = fields[4].GetUInt32();
        // quest might be 0 to enable spellclick active infinity after start quest
        if (quest_end)
        {
            if (mQuestTemplates.find(quest_end) == mQuestTemplates.end())
            {
                sLog->outErrorDb("Table npc_spellclick_spells references unknown end quest %u. Skipping entry.", quest_end);
                continue;
            }
        }

        uint8 userType = fields[8].GetUInt8();
        if (userType >= SPELL_CLICK_USER_MAX)
            sLog->outErrorDb("Table npc_spellclick_spells references unknown user type %u. Skipping entry.", uint32(userType));

        uint8 castFlags = fields[5].GetUInt8();
        SpellClickInfo info;
        info.spellId = spellid;
        info.questStart = quest_start;
        info.questStartCanActive = quest_start_active;
        info.questEnd = quest_end;
        info.castFlags = castFlags;
        info.auraRequired = auraRequired;
        info.auraForbidden = auraForbidden;
        info.userType = SpellClickUserTypes(userType);
        mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info));

        // mark creature template as spell clickable
        const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK;

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u spellclick definitions", count);
}

void ObjectMgr::SaveCreatureRespawnTime (uint32 loguid, uint32 instance, time_t t)
{
    if (!t)
    {
        // Delete only
        RemoveCreatureRespawnTime(loguid, instance);
        return;
    }

    // This function can be called from various map threads concurrently
    {
        m_CreatureRespawnTimesMtx.acquire();
        mCreatureRespawnTimes[MAKE_PAIR64(loguid, instance)] = t;
        m_CreatureRespawnTimesMtx.release();
    }

    PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_CREATURE_RESPAWN);
    stmt->setUInt32(0, loguid);
    stmt->setUInt64(1, uint64(t));
    stmt->setUInt32(2, instance);
    CharacterDatabase.Execute(stmt);
}

void ObjectMgr::RemoveCreatureRespawnTime (uint32 loguid, uint32 instance)
{
    // This function can be called from various map threads concurrently
    {
        m_CreatureRespawnTimesMtx.acquire();
        mCreatureRespawnTimes[MAKE_PAIR64(loguid, instance)] = 0;
        m_CreatureRespawnTimesMtx.release();
    }

    PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
    stmt->setUInt32(0, loguid);
    stmt->setUInt32(1, instance);
    CharacterDatabase.Execute(stmt);
}

void ObjectMgr::DeleteCreatureData (uint32 guid)
{
    // remove mapid*cellid -> guid_set map
    CreatureData const* data = GetCreatureData(guid);
    if (data)
        RemoveCreatureFromGrid(guid, data);

    mCreatureDataMap.erase(guid);
}

void ObjectMgr::SaveGORespawnTime (uint32 loguid, uint32 instance, time_t t)
{
    if (!t)
    {
        // Delete only
        RemoveGORespawnTime(loguid, instance);
        return;
    }

    // This function can be called from different map threads concurrently
    {
        m_GORespawnTimesMtx.acquire();
        mGORespawnTimes[MAKE_PAIR64(loguid, instance)] = t;
        m_GORespawnTimesMtx.release();
    }

    PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_ADD_GO_RESPAWN);
    stmt->setUInt32(0, loguid);
    stmt->setUInt64(1, uint64(t));
    stmt->setUInt32(2, instance);
    CharacterDatabase.Execute(stmt);
}

void ObjectMgr::RemoveGORespawnTime (uint32 loguid, uint32 instance)
{
    // This function can be called from different map threads concurrently
    {
        m_GORespawnTimesMtx.acquire();
        mGORespawnTimes[MAKE_PAIR64(loguid, instance)] = 0;
        m_GORespawnTimesMtx.release();
    }

    PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
    stmt->setUInt32(0, loguid);
    stmt->setUInt32(1, instance);
    CharacterDatabase.Execute(stmt);
}

void ObjectMgr::DeleteRespawnTimeForInstance (uint32 instance)
{
    // This function can be called from different map threads concurrently
    RespawnTimes::iterator next;

    {
        m_GORespawnTimesMtx.acquire();
        for (RespawnTimes::iterator itr = mGORespawnTimes.begin(); itr != mGORespawnTimes.end(); itr = next)
        {
            next = itr;
            ++next;

            if (GUID_HIPART(itr->first) == instance)
                mGORespawnTimes.erase(itr);
        }
        m_GORespawnTimesMtx.release();
    }
    {
        m_CreatureRespawnTimesMtx.acquire();
        for (RespawnTimes::iterator itr = mCreatureRespawnTimes.begin(); itr != mCreatureRespawnTimes.end(); itr = next)
        {
            next = itr;
            ++next;

            if (GUID_HIPART(itr->first) == instance)
                mCreatureRespawnTimes.erase(itr);
        }
        m_CreatureRespawnTimesMtx.release();
    }
    CharacterDatabase.PExecute("DELETE FROM creature_respawn WHERE instanceId = '%u'", instance);
    CharacterDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instanceId = '%u'", instance);
}

void ObjectMgr::DeleteGOData (uint32 guid)
{
    // remove mapid*cellid -> guid_set map
    GameObjectData const* data = GetGOData(guid);
    if (data)
        RemoveGameobjectFromGrid(guid, data);

    mGameObjectDataMap.erase(guid);
}

void ObjectMgr::AddCorpseCellData (uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance)
{
    // corpses are always added to spawn mode 0 and they are spawned by their instance id
    CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid, 0)][cellid];
    cell_guids.corpses[player_guid] = instance;
}

void ObjectMgr::DeleteCorpseCellData (uint32 mapid, uint32 cellid, uint32 player_guid)
{
    // corpses are always added to spawn mode 0 and they are spawned by their instance id
    CellObjectGuids& cell_guids = mMapObjectGuids[MAKE_PAIR32(mapid, 0)][cellid];
    cell_guids.corpses.erase(player_guid);
}

void ObjectMgr::LoadQuestRelationsHelper (QuestRelations& map, std::string table, bool starter, bool go)
{
    map.clear();          // need for reload case

    uint32 count = 0;

    QueryResult result = WorldDatabase.PQuery("SELECT id, quest, pool_entry FROM %s qr LEFT JOIN pool_quest pq ON qr.quest = pq.entry", table.c_str());

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded 0 quest relations from %s, table is empty.", table.c_str());
        return;
    }

    PooledQuestRelation* poolRelationMap = go ? &sPoolMgr->mQuestGORelation : &sPoolMgr->mQuestCreatureRelation;
    if (starter)
        poolRelationMap->clear();

    do
    {
        uint32 id = result->Fetch()[0].GetUInt32();
        uint32 quest = result->Fetch()[1].GetUInt32();
        uint32 poolId = result->Fetch()[2].GetUInt32();

        if (mQuestTemplates.find(quest) == mQuestTemplates.end())
        {
            sLog->outErrorDb("Table `%s`: Quest %u listed for entry %u does not exist.", table.c_str(), quest, id);
            continue;
        }

        if (!poolId || !starter)
            map.insert(QuestRelations::value_type(id, quest));
        else if (starter)
            poolRelationMap->insert(PooledQuestRelation::value_type(quest, id));

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u quest relations from %s", count, table.c_str());
}

void ObjectMgr::LoadGameobjectQuestRelations ()
{
    LoadQuestRelationsHelper(mGOQuestRelations, "gameobject_questrelation", true, true);

    for (QuestRelations::iterator itr = mGOQuestRelations.begin(); itr != mGOQuestRelations.end(); ++itr)
    {
        GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
        if (!goInfo)
            sLog->outErrorDb("Table `gameobject_questrelation` have data for not existed gameobject entry (%u) and existed quest %u", itr->first, itr->second);
        else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
            sLog->outErrorDb("Table `gameobject_questrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
    }
}

void ObjectMgr::LoadGameobjectInvolvedRelations ()
{
    LoadQuestRelationsHelper(mGOQuestInvolvedRelations, "gameobject_involvedrelation", false, true);

    for (QuestRelations::iterator itr = mGOQuestInvolvedRelations.begin(); itr != mGOQuestInvolvedRelations.end(); ++itr)
    {
        GameObjectInfo const* goInfo = GetGameObjectInfo(itr->first);
        if (!goInfo)
            sLog->outErrorDb("Table `gameobject_involvedrelation` have data for not existed gameobject entry (%u) and existed quest %u", itr->first, itr->second);
        else if (goInfo->type != GAMEOBJECT_TYPE_QUESTGIVER)
            sLog->outErrorDb("Table `gameobject_involvedrelation` have data gameobject entry (%u) for quest %u, but GO is not GAMEOBJECT_TYPE_QUESTGIVER", itr->first, itr->second);
    }
}

void ObjectMgr::LoadCreatureQuestRelations ()
{
    LoadQuestRelationsHelper(mCreatureQuestRelations, "creature_questrelation", true, false);

    for (QuestRelations::iterator itr = mCreatureQuestRelations.begin(); itr != mCreatureQuestRelations.end(); ++itr)
    {
        CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
        if (!cInfo)
            sLog->outErrorDb("Table `creature_questrelation` have data for not existed creature entry (%u) and existed quest %u", itr->first, itr->second);
        else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
            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);
    }
}

void ObjectMgr::LoadCreatureInvolvedRelations ()
{
    LoadQuestRelationsHelper(mCreatureQuestInvolvedRelations, "creature_involvedrelation", false, false);

    for (QuestRelations::iterator itr = mCreatureQuestInvolvedRelations.begin(); itr != mCreatureQuestInvolvedRelations.end(); ++itr)
    {
        CreatureInfo const* cInfo = GetCreatureTemplate(itr->first);
        if (!cInfo)
            sLog->outErrorDb("Table `creature_involvedrelation` have data for not existed creature entry (%u) and existed quest %u", itr->first, itr->second);
        else if (!(cInfo->npcflag & UNIT_NPC_FLAG_QUESTGIVER))
            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);
    }
}

void ObjectMgr::LoadReservedPlayersNames ()
{
    m_ReservedNames.clear();          // need for reload case

    QueryResult result = WorldDatabase.Query("SELECT name FROM reserved_name");

    uint32 count = 0;

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded %u reserved player names", count);
        return;
    }

    Field* fields;
    do
    {
        fields = result->Fetch();
        std::string name = fields[0].GetString();

        std::wstring wstr;
        if (!Utf8toWStr(name, wstr))
        {
            sLog->outError("Table `reserved_name` have invalid name: %s", name.c_str());
            continue;
        }

        wstrToLower(wstr);

        m_ReservedNames.insert(wstr);
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u reserved player names", count);
}

bool ObjectMgr::IsReservedName (const std::string& name) const
{
    std::wstring wstr;
    if (!Utf8toWStr(name, wstr))
        return false;

    wstrToLower(wstr);

    return m_ReservedNames.find(wstr) != m_ReservedNames.end();
}

enum LanguageType
{
    LT_BASIC_LATIN = 0x0000, LT_EXTENDEN_LATIN = 0x0001, LT_CYRILLIC = 0x0002, LT_EAST_ASIA = 0x0004, LT_ANY = 0xFFFF
};

static LanguageType GetRealmLanguageType (bool create)
{
    switch (sWorld->getIntConfig(CONFIG_REALM_ZONE))
    {
    case REALM_ZONE_UNKNOWN:          // any language
    case REALM_ZONE_DEVELOPMENT:
    case REALM_ZONE_TEST_SERVER:
    case REALM_ZONE_QA_SERVER:
        return LT_ANY;
    case REALM_ZONE_UNITED_STATES:          // extended-Latin
    case REALM_ZONE_OCEANIC:
    case REALM_ZONE_LATIN_AMERICA:
    case REALM_ZONE_ENGLISH:
    case REALM_ZONE_GERMAN:
    case REALM_ZONE_FRENCH:
    case REALM_ZONE_SPANISH:
        return LT_EXTENDEN_LATIN;
    case REALM_ZONE_KOREA:          // East-Asian
    case REALM_ZONE_TAIWAN:
    case REALM_ZONE_CHINA:
        return LT_EAST_ASIA;
    case REALM_ZONE_RUSSIAN:          // Cyrillic
        return LT_CYRILLIC;
    default:
        return create ? LT_BASIC_LATIN : LT_ANY;          // basic-Latin at create, any at login
    }
}

bool isValidString (std::wstring wstr, uint32 strictMask, bool numericOrSpace, bool create = false)
{
    if (strictMask == 0)          // any language, ignore realm
    {
        if (isExtendedLatinString(wstr, numericOrSpace))
            return true;
        if (isCyrillicString(wstr, numericOrSpace))
            return true;
        if (isEastAsianString(wstr, numericOrSpace))
            return true;
        return false;
    }

    if (strictMask & 0x2)          // realm zone specific
    {
        LanguageType lt = GetRealmLanguageType(create);
        if (lt & LT_EXTENDEN_LATIN)
            if (isExtendedLatinString(wstr, numericOrSpace))
                return true;
        if (lt & LT_CYRILLIC)
            if (isCyrillicString(wstr, numericOrSpace))
                return true;
        if (lt & LT_EAST_ASIA)
            if (isEastAsianString(wstr, numericOrSpace))
                return true;
    }

    if (strictMask & 0x1)          // basic Latin
    {
        if (isBasicLatinString(wstr, numericOrSpace))
            return true;
    }

    return false;
}

uint8 ObjectMgr::CheckPlayerName (const std::string& name, bool create)
{
    std::wstring wname;
    if (!Utf8toWStr(name, wname))
        return CHAR_NAME_INVALID_CHARACTER;

    if (wname.size() > MAX_PLAYER_NAME)
        return CHAR_NAME_TOO_LONG;

    uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PLAYER_NAME);
    if (wname.size() < minName)
        return CHAR_NAME_TOO_SHORT;

    uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PLAYER_NAMES);
    if (!isValidString(wname, strictMask, false, create))
        return CHAR_NAME_MIXED_LANGUAGES;

    return CHAR_NAME_SUCCESS;
}

bool ObjectMgr::IsValidCharterName (const std::string& name)
{
    std::wstring wname;
    if (!Utf8toWStr(name, wname))
        return false;

    if (wname.size() > MAX_CHARTER_NAME)
        return false;

    uint32 minName = sWorld->getIntConfig(CONFIG_MIN_CHARTER_NAME);
    if (wname.size() < minName)
        return false;

    uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_CHARTER_NAMES);

    return isValidString(wname, strictMask, true);
}

PetNameInvalidReason ObjectMgr::CheckPetName (const std::string& name)
{
    std::wstring wname;
    if (!Utf8toWStr(name, wname))
        return PET_NAME_INVALID;

    if (wname.size() > MAX_PET_NAME)
        return PET_NAME_TOO_LONG;

    uint32 minName = sWorld->getIntConfig(CONFIG_MIN_PET_NAME);
    if (wname.size() < minName)
        return PET_NAME_TOO_SHORT;

    uint32 strictMask = sWorld->getIntConfig(CONFIG_STRICT_PET_NAMES);
    if (!isValidString(wname, strictMask, false))
        return PET_NAME_MIXED_LANGUAGES;

    return PET_NAME_SUCCESS;
}

void ObjectMgr::LoadGameObjectForQuests ()
{
    mGameObjectForQuestSet.clear();          // need for reload case

    if (!sGOStorage.MaxEntry)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 GameObjects for quests");
        return;
    }

    uint32 count = 0;

    // collect GO entries for GO that must activated
    for (uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
    {
        GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
        if (!goInfo)
            continue;

        switch (goInfo->type)
        {
        // scan GO chest with loot including quest items
        case GAMEOBJECT_TYPE_CHEST:
        {
            uint32 loot_id = goInfo->GetLootId();

            // find quest loot for GO
            if (goInfo->chest.questId || LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
            {
                mGameObjectForQuestSet.insert(go_entry);
                ++count;
            }
            break;
        }
        case GAMEOBJECT_TYPE_GENERIC:
        {
            if (goInfo->_generic.questID > 0)          //quests objects
            {
                mGameObjectForQuestSet.insert(go_entry);
                count++;
            }
            break;
        }
        case GAMEOBJECT_TYPE_GOOBER:
        {
            if (goInfo->goober.questId > 0)          //quests objects
            {
                mGameObjectForQuestSet.insert(go_entry);
                count++;
            }
            break;
        }
        default:
            break;
        }
    }

    sLog->outString();
    sLog->outString(">> Loaded %u GameObjects for quests", count);
}

bool ObjectMgr::LoadArkCoreStrings (char const* table, int32 min_value, int32 max_value)
{
    int32 start_value = min_value;
    int32 end_value = max_value;
    // some string can have negative indexes range
    if (start_value < 0)
    {
        if (end_value >= start_value)
        {
            sLog->outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
            return false;
        }

        // real range (max+1, min+1) exaple: (-10, -1000) -> -999...-10+1
        std::swap(start_value, end_value);
        ++start_value;
        ++end_value;
    }
    else
    {
        if (start_value >= end_value)
        {
            sLog->outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.", table, min_value, max_value);
            return false;
        }
    }

    // cleanup affected map part for reloading case
    for (ArkCoreStringLocaleMap::iterator itr = mArkCoreStringLocaleMap.begin(); itr != mArkCoreStringLocaleMap.end();)
    {
        if (itr->first >= start_value && itr->first < end_value)
            mArkCoreStringLocaleMap.erase(itr++);
        else
            ++itr;
    }

    QueryResult result = WorldDatabase.PQuery("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8 FROM %s", table);

    if (!result)
    {
        sLog->outString();
        if (min_value == MIN_ARKCORE_STRING_ID)          // error only in case internal strings
            sLog->outErrorDb(">> Loaded 0 ArkCORE Strings. DB table `%s` is empty. Cannot continue.", table);
        else
            sLog->outString(">> Loaded 0 string templates. DB table `%s` is empty.", table);
        return false;
    }

    uint32 count = 0;

    do
    {
        Field *fields = result->Fetch();

        int32 entry = fields[0].GetInt32();

        if (entry == 0)
        {
            sLog->outErrorDb("Table `%s` contain reserved entry 0, ignored.", table);
            continue;
        }
        else if (entry < start_value || entry >= end_value)
        {
            sLog->outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.", table, entry, min_value, max_value);
            continue;
        }

        ArkCoreStringLocale& data = mArkCoreStringLocaleMap[entry];

        if (data.Content.size() > 0)
        {
            sLog->outErrorDb("Table `%s` contain data for already loaded entry  %i (from another table?), ignored.", table, entry);
            continue;
        }

        data.Content.resize(1);
        ++count;

        for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
        {
            std::string str = fields[i + 1].GetString();
            AddLocaleString(str, LocaleConstant(i), data.Content);
        }
    }
    while (result->NextRow());

    sLog->outString();
    if (min_value == MIN_ARKCORE_STRING_ID)
        sLog->outString(">> Loaded %u ArkCORE Strings from table %s", count, table);
    else
        sLog->outString(">> Loaded %u string templates from %s", count, table);

    return true;
}

const char *ObjectMgr::GetArkCoreString (int32 entry, LocaleConstant locale_idx) const
{
    if (ArkCoreStringLocale const *msl = GetArkCoreStringLocale(entry))
    {
        if (msl->Content.size() > size_t(locale_idx) && !msl->Content[locale_idx].empty())
            return msl->Content[locale_idx].c_str();

        return msl->Content[DEFAULT_LOCALE].c_str();
    }

    if (entry > 0)
        sLog->outErrorDb("Entry %i not found in `arkcore_string` table.", entry);
    else
        sLog->outErrorDb("Trinity string entry %i not found in DB.", entry);
    return "<error>";
}

void ObjectMgr::LoadFishingBaseSkillLevel ()
{
    mFishingBaseForArea.clear();          // for reload case

    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT entry, skill FROM skill_fishing_base_level");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `skill_fishing_base_level`, table is empty!");
        return;
    }

    do
    {
        Field *fields = result->Fetch();
        uint32 entry = fields[0].GetUInt32();
        int32 skill = fields[1].GetInt32();

        AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry);
        if (!fArea)
        {
            sLog->outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist", entry);
            continue;
        }

        mFishingBaseForArea[entry] = skill;
        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u areas for fishing base skill level", count);
}

bool ObjectMgr::CheckDeclinedNames (std::wstring mainpart, DeclinedName const& names)
{
    for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
    {
        std::wstring wname;
        if (!Utf8toWStr(names.name[i], wname))
            return false;

        if (mainpart != GetMainPartOfName(wname, i + 1))
            return false;
    }
    return true;
}

uint32 ObjectMgr::GetAreaTriggerScriptId (uint32 trigger_id)
{
    AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(trigger_id);
    if (i != mAreaTriggerScripts.end())
        return i->second;
    return 0;
}

SpellScriptsBounds ObjectMgr::GetSpellScriptsBounds (uint32 spell_id)
{
    return SpellScriptsBounds(mSpellScripts.lower_bound(spell_id), mSpellScripts.upper_bound(spell_id));
}

SkillRangeType GetSkillRangeType (SkillLineEntry const *pSkill, bool racial)
{
    switch (pSkill->categoryId)
    {
    case SKILL_CATEGORY_LANGUAGES:
        return SKILL_RANGE_LANGUAGE;
    case SKILL_CATEGORY_WEAPON:
        if (pSkill->id != SKILL_FIST_WEAPONS)
            return SKILL_RANGE_LEVEL;
        else
            return SKILL_RANGE_MONO;
    case SKILL_CATEGORY_ARMOR:
    case SKILL_CATEGORY_CLASS:
        if (pSkill->id != SKILL_LOCKPICKING)
            return SKILL_RANGE_MONO;
        else
            return SKILL_RANGE_LEVEL;
    case SKILL_CATEGORY_SECONDARY:
    case SKILL_CATEGORY_PROFESSION:
        // not set skills for professions and racial abilities
        if (IsProfessionSkill(pSkill->id))
            return SKILL_RANGE_RANK;
        else if (racial)
            return SKILL_RANGE_NONE;
        else
            return SKILL_RANGE_MONO;
    default:
    case SKILL_CATEGORY_ATTRIBUTES:          //not found in dbc
    case SKILL_CATEGORY_GENERIC:          //only GENERIC(DND)
        return SKILL_RANGE_NONE;
    }
}

void ObjectMgr::LoadGameTele ()
{
    m_GameTeleMap.clear();          // for reload case

    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `game_tele`, table is empty!");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint32 id = fields[0].GetUInt32();

        GameTele gt;

        gt.position_x = fields[1].GetFloat();
        gt.position_y = fields[2].GetFloat();
        gt.position_z = fields[3].GetFloat();
        gt.orientation = fields[4].GetFloat();
        gt.mapId = fields[5].GetUInt32();
        gt.name = fields[6].GetString();

        if (!MapManager::IsValidMapCoord(gt.mapId, gt.position_x, gt.position_y, gt.position_z, gt.orientation))
        {
            sLog->outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.", id, gt.name.c_str());
            continue;
        }

        if (!Utf8toWStr(gt.name, gt.wnameLow))
        {
            sLog->outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.", id);
            continue;
        }

        wstrToLower(gt.wnameLow);

        m_GameTeleMap[id] = gt;

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u GameTeleports", count);
}

GameTele const* ObjectMgr::GetGameTele (const std::string& name) const
{
    // explicit name case
    std::wstring wname;
    if (!Utf8toWStr(name, wname))
        return false;

    // converting string that we try to find to lower case
    wstrToLower(wname);

    // Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
    const GameTele* alt = NULL;
    for (GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
    {
        if (itr->second.wnameLow == wname)
            return &itr->second;
        else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
            alt = &itr->second;
    }

    return alt;
}

bool ObjectMgr::AddGameTele (GameTele& tele)
{
    // find max id
    uint32 new_id = 0;
    for (GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
        if (itr->first > new_id)
            new_id = itr->first;

    // use next
    ++new_id;

    if (!Utf8toWStr(tele.name, tele.wnameLow))
        return false;

    wstrToLower(tele.wnameLow);

    m_GameTeleMap[new_id] = tele;

    WorldDatabase.PExecute("INSERT INTO game_tele (id, position_x, position_y, position_z, orientation, map, name) VALUES (%u, %f, %f, %f, %f, %d, '%s')", new_id, tele.position_x, tele.position_y, tele.position_z, tele.orientation, tele.mapId, tele.name.c_str());
    return true;
}

bool ObjectMgr::DeleteGameTele (const std::string& name)
{
    // explicit name case
    std::wstring wname;
    if (!Utf8toWStr(name, wname))
        return false;

    // converting string that we try to find to lower case
    wstrToLower(wname);

    for (GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
    {
        if (itr->second.wnameLow == wname)
        {
            WorldDatabase.PExecute("DELETE FROM game_tele WHERE name = '%s'", itr->second.name.c_str());
            m_GameTeleMap.erase(itr);
            return true;
        }
    }

    return false;
}

void ObjectMgr::LoadMailLevelRewards ()
{
    m_mailLevelRewardMap.clear();          // for reload case

    uint32 count = 0;
    QueryResult result = WorldDatabase.Query("SELECT level, raceMask, mailTemplateId, senderEntry FROM mail_level_reward");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `mail_level_reward`, table is empty!");
        return;
    }

    do
    {
        Field *fields = result->Fetch();

        uint8 level = fields[0].GetUInt8();
        uint32 raceMask = fields[1].GetUInt32();
        uint32 mailTemplateId = fields[2].GetUInt32();
        uint32 senderEntry = fields[3].GetUInt32();

        if (level > MAX_LEVEL)
        {
            sLog->outErrorDb("Table `mail_level_reward` have data for level %u that more supported by client (%u), ignoring.", level, MAX_LEVEL);
            continue;
        }

        if (!(raceMask & RACEMASK_ALL_PLAYABLE))
        {
            sLog->outErrorDb("Table `mail_level_reward` have raceMask (%u) for level %u that not include any player races, ignoring.", raceMask, level);
            continue;
        }

        if (!sMailTemplateStore.LookupEntry(mailTemplateId))
        {
            sLog->outErrorDb("Table `mail_level_reward` have invalid mailTemplateId (%u) for level %u that invalid not include any player races, ignoring.", mailTemplateId, level);
            continue;
        }

        if (!GetCreatureTemplateStore(senderEntry))
        {
            sLog->outErrorDb("Table `mail_level_reward` have not existed sender creature entry (%u) for level %u that invalid not include any player races, ignoring.", senderEntry, level);
            continue;
        }

        m_mailLevelRewardMap[level].push_back(MailLevelReward(raceMask, mailTemplateId, senderEntry));

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u level dependent mail rewards, ", count);
}

void ObjectMgr::AddSpellToTrainer (uint32 entry, uint32 spell, uint32 spellCost, uint32 reqSkill, uint32 reqSkillValue, uint32 reqLevel, uint32 KillCredit)
{
    if (entry >= ARKCORE_TRAINER_START_REF)
        return;

    CreatureInfo const* cInfo = GetCreatureTemplate(entry);
    if (!cInfo)
    {
        sLog->outErrorDb("Table `npc_trainer` contains an entry for a non-existing creature template (Entry: %u), ignoring", entry);
        return;
    }

    if (!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
    {
        sLog->outErrorDb("Table `npc_trainer` contains an entry for a creature template (Entry: %u) without trainer flag, ignoring", entry);
        return;
    }

    SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
    if (!spellinfo)
    {
        sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u), ignoring", entry, spell);
        return;
    }

    if (!SpellMgr::IsSpellValid(spellinfo))
    {
        sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a broken spell (Spell: %u), ignoring", entry, spell);
        return;
    }

    if (GetTalentSpellCost(spell))
    {
        sLog->outErrorDb("Table `npc_trainer` contains an entry (Entry: %u) for a non-existing spell (Spell: %u) which is a talent, ignoring", entry, spell);
        return;
    }

    TrainerSpellData& data = m_mCacheTrainerSpellMap[entry];

    TrainerSpell& trainerSpell = data.spellList[spell];
    trainerSpell.spell = spell;
    trainerSpell.spellCost = spellCost;
    trainerSpell.reqSkill = reqSkill;
    trainerSpell.reqSkillValue = reqSkillValue;
    trainerSpell.reqLevel = reqLevel;
    trainerSpell.KillCredit = KillCredit;

    if (!trainerSpell.reqLevel)
        trainerSpell.reqLevel = spellinfo->spellLevel;

    // calculate learned spell for profession case when stored cast-spell
    trainerSpell.learnedSpell[0] = spell;
    for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
    {
        if (spellinfo->Effect[i] != SPELL_EFFECT_LEARN_SPELL)
            continue;
        if (trainerSpell.learnedSpell[0] == spell)
            trainerSpell.learnedSpell[0] = 0;
        // player must be able to cast spell on himself
        if (spellinfo->EffectImplicitTargetA[i] != 0 && spellinfo->EffectImplicitTargetA[i] != TARGET_UNIT_TARGET_ALLY && spellinfo->EffectImplicitTargetA[i] != TARGET_UNIT_TARGET_ANY && spellinfo->EffectImplicitTargetA[i] != TARGET_UNIT_CASTER)
        {
            sLog->outErrorDb("Table `npc_trainer` has spell %u for trainer entry %u with learn effect which has incorrect target type, ignoring learn effect!", spell, entry);
            continue;
        }

        trainerSpell.learnedSpell[i] = spellinfo->EffectTriggerSpell[i];

        if (trainerSpell.learnedSpell[i] && SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell[i]))
            data.trainerType = 2;
    }

    return;
}

void ObjectMgr::LoadTrainerSpell ()
{
    // For reload case
    m_mCacheTrainerSpellMap.clear();

    std::set<uint32> skip_trainers;

    QueryResult result = WorldDatabase.Query("SELECT b.entry, a.spell, a.spellcost, a.reqskill, a.reqskillvalue, a.reqlevel, a.KillCredit FROM npc_trainer AS a "
            "INNER JOIN npc_trainer AS b ON a.entry = -(b.spell) "
            "UNION SELECT * FROM npc_trainer WHERE spell > 0");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `npc_trainer`, table is empty!");
        return;
    }

    uint32 count = 0;
    do
    {
        Field* fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();
        uint32 spell = fields[1].GetUInt32();
        uint32 spellCost = fields[2].GetUInt32();
        uint32 reqSkill = fields[3].GetUInt32();
        uint32 reqSkillValue = fields[4].GetUInt32();
        uint32 reqLevel = fields[5].GetUInt32();
        uint32 KillCredit = fields[6].GetUInt32();

        AddSpellToTrainer(entry, spell, spellCost, reqSkill, reqSkillValue, reqLevel, KillCredit);

        count++;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %d Trainers", count);
}

int ObjectMgr::LoadReferenceVendor (int32 vendor, int32 item, std::set<uint32> *skip_vendors)
{
    // find all items from the reference vendor
    QueryResult result = WorldDatabase.PQuery("SELECT item, maxcount, incrtime, ExtendedCost FROM npc_vendor WHERE entry='%d' ORDER BY slot ASC", item);
    if (!result)
        return 0;

    uint32 count = 0;
    do
    {
        Field* fields = result->Fetch();

        int32 item_id = fields[0].GetInt32();

        // if item is a negative, its a reference
        if (item_id < 0)
            count += LoadReferenceVendor(vendor, -item_id, skip_vendors);
        else
        {
            int32 maxcount = fields[1].GetInt32();
            uint32 incrtime = fields[2].GetUInt32();
            uint32 ExtendedCost = fields[3].GetUInt32();

            if (!IsVendorItemValid(vendor, item_id, maxcount, incrtime, ExtendedCost, NULL, skip_vendors))
                continue;

            VendorItemData& vList = m_mCacheVendorItemMap[vendor];

            vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
            ++count;
        }
    }
    while (result->NextRow());

    return count;
}

void ObjectMgr::LoadVendors ()
{
    // For reload case
    for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
        itr->second.Clear();
    m_mCacheVendorItemMap.clear();

    std::set<uint32> skip_vendors;

    QueryResult result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor ORDER BY entry, slot ASC");
    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `npc_vendor`, table is empty!");
        return;
    }

    uint32 count = 0;
    do
    {
        Field* fields = result->Fetch();

        uint32 entry = fields[0].GetUInt32();
        int32 item_id = fields[1].GetInt32();

        // if item is a negative, its a reference
        if (item_id < 0)
            count += LoadReferenceVendor(entry, -item_id, &skip_vendors);
        else
        {
            int32 maxcount = fields[2].GetInt32();
            uint32 incrtime = fields[3].GetUInt32();
            uint32 ExtendedCost = fields[4].GetUInt32();

            if (!IsVendorItemValid(entry, item_id, maxcount, incrtime, ExtendedCost, NULL, &skip_vendors))
                continue;

            VendorItemData& vList = m_mCacheVendorItemMap[entry];

            vList.AddItem(item_id, maxcount, incrtime, ExtendedCost);
            ++count;
        }
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %d Vendors ", count);
}

void ObjectMgr::LoadGossipMenu ()
{
    m_mGossipMenusMap.clear();

    QueryResult result = WorldDatabase.Query("SELECT entry, text_id FROM gossip_menu");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded `gossip_menu`, table is empty!");
        return;
    }

    uint32 count = 0;

    do
    {
        Field* fields = result->Fetch();

        GossipMenus gMenu;

        gMenu.entry = fields[0].GetUInt32();
        gMenu.text_id = fields[1].GetUInt32();

        if (!GetGossipText(gMenu.text_id))
        {
            sLog->outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
            continue;
        }

        m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu));

        ++count;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u gossip_menu entries", count);
}

void ObjectMgr::LoadGossipMenuItems ()
{
    m_mGossipMenuItemsMap.clear();

    QueryResult result = WorldDatabase.Query("SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, "
            "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text "
            "FROM gossip_menu_option ORDER BY menu_id, id");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded gossip_menu_option, table is empty!");
        return;
    }

    uint32 count = 0;

    std::set<uint32> gossipScriptSet;

    for (ScriptMapMap::const_iterator itr = sGossipScripts.begin(); itr != sGossipScripts.end(); ++itr)
        gossipScriptSet.insert(itr->first);

    do
    {
        Field* fields = result->Fetch();

        GossipMenuItems gMenuItem;

        gMenuItem.menu_id = fields[0].GetUInt32();
        gMenuItem.id = fields[1].GetUInt32();
        gMenuItem.option_icon = fields[2].GetUInt8();
        gMenuItem.option_text = fields[3].GetString();
        gMenuItem.option_id = fields[4].GetUInt32();
        gMenuItem.npc_option_npcflag = fields[5].GetUInt32();
        gMenuItem.action_menu_id = fields[6].GetUInt32();
        gMenuItem.action_poi_id = fields[7].GetUInt32();
        gMenuItem.action_script_id = fields[8].GetUInt32();
        gMenuItem.box_coded = fields[9].GetUInt8() != 0;
        gMenuItem.box_money = fields[10].GetUInt32();
        gMenuItem.box_text = fields[11].GetString();

        if (gMenuItem.option_icon >= GOSSIP_ICON_MAX)
        {
            sLog->outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_icon);
            gMenuItem.option_icon = GOSSIP_ICON_CHAT;
        }

        if (gMenuItem.option_id >= GOSSIP_OPTION_MAX)
            sLog->outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown option id %u. Option will not be used", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_id);

        if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id))
        {
            sLog->outErrorDb("Table gossip_menu_option for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_poi_id);
            gMenuItem.action_poi_id = 0;
        }

        if (gMenuItem.action_script_id)
        {
            if (gMenuItem.option_id != GOSSIP_OPTION_GOSSIP)
            {
                sLog->outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u but option_id is not GOSSIP_OPTION_GOSSIP, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_script_id);
                continue;
            }

            if (sGossipScripts.find(gMenuItem.action_script_id) == sGossipScripts.end())
            {
                sLog->outErrorDb("Table gossip_menu_option for menu %u, id %u have action_script_id %u that does not exist in `gossip_scripts`, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_script_id);
                continue;
            }

            gossipScriptSet.erase(gMenuItem.action_script_id);
        }

        m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem));

        ++count;
    }
    while (result->NextRow());

    if (!gossipScriptSet.empty())
    {
        for (std::set<uint32>::const_iterator itr = gossipScriptSet.begin(); itr != gossipScriptSet.end(); ++itr)
            sLog->outErrorDb("Table `gossip_scripts` contain unused script, id %u.", *itr);
    }

    sLog->outString();
    sLog->outString(">> Loaded %u gossip_menu_option entries", count);
}

void ObjectMgr::AddVendorItem (uint32 entry, uint32 item, int32 maxcount, uint32 incrtime, uint32 extendedcost, bool savetodb)
{
    VendorItemData& vList = m_mCacheVendorItemMap[entry];
    vList.AddItem(item, maxcount, incrtime, extendedcost);

    if (savetodb)
        WorldDatabase.PExecute("INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost) VALUES('%u', '%u', '%u', '%u', '%u')", entry, item, maxcount, incrtime, extendedcost);
}

bool ObjectMgr::RemoveVendorItem (uint32 entry, uint32 item, bool savetodb)
{
    CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
    if (iter == m_mCacheVendorItemMap.end())
        return false;

    if (!iter->second.RemoveItem(item))
        return false;

    if (savetodb)
        WorldDatabase.PExecute("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'", entry, item);
    return true;
}

bool ObjectMgr::IsVendorItemValid (uint32 vendor_entry, uint32 item_id, int32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set<uint32>* skip_vendors, uint32 ORnpcflag) const
{
    CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
    if (!cInfo)
    {
        if (pl)
            ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
        else
            sLog->outErrorDb("Table `(game_event_)npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
        return false;
    }

    if (!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
    {
        if (!skip_vendors || skip_vendors->count(vendor_entry) == 0)
        {
            if (pl)
                ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
            else
                sLog->outErrorDb("Table `(game_event_)npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);

            if (skip_vendors)
                skip_vendors->insert(vendor_entry);
        }
        return false;
    }

    if (!ObjectMgr::GetItemPrototype(item_id))
    {
        if (pl)
            ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
        else
            sLog->outErrorDb("Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore", vendor_entry, item_id);
        return false;
    }

    if (ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
    {
        if (pl)
            ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST, ExtendedCost);
        else
            sLog->outErrorDb("Table `(game_event_)npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore", item_id, ExtendedCost, vendor_entry);
        return false;
    }

    if (maxcount > 0 && incrtime == 0)
    {
        if (pl)
            ChatHandler(pl).PSendSysMessage("MaxCount != 0 (%u) but IncrTime == 0", maxcount);
        else
            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);
        return false;
    }
    else if (maxcount == 0 && incrtime > 0)
    {
        if (pl)
            ChatHandler(pl).PSendSysMessage("MaxCount == 0 but IncrTime<>= 0");
        else
            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);
        return false;
    }

    VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
    if (!vItems)
        return true;          // later checks for non-empty lists

    if (vItems->FindItemCostPair(item_id, ExtendedCost))
    {
        if (pl)
            ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST, item_id, ExtendedCost);
        else
            sLog->outErrorDb("Table `npc_vendor` has duplicate items %u (with extended cost %u) for vendor (Entry: %u), ignoring", item_id, ExtendedCost, vendor_entry);
        return false;
    }

    if (vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
    {
        if (pl)
            ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
        else
            sLog->outErrorDb("Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
        return false;
    }

    return true;
}

void ObjectMgr::LoadScriptNames ()
{
    m_scriptNames.push_back("");
    QueryResult result = WorldDatabase.Query("SELECT DISTINCT(ScriptName) FROM achievement_criteria_data WHERE ScriptName <> '' AND type = 11 "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM item_template WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' "
            "UNION "
            "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");

    if (!result)
    {
        sLog->outString();
        sLog->outErrorDb(">> Loaded empty set of Script Names!");
        return;
    }

    uint32 count = 1;

    do
    {
        m_scriptNames.push_back((*result)[0].GetString());
        ++count;
    }
    while (result->NextRow());

    std::sort(m_scriptNames.begin(), m_scriptNames.end());
    sLog->outString();
    sLog->outString(">> Loaded %d Script Names", count);
}

uint32 ObjectMgr::GetScriptId (const char *name)
{
    // use binary search to find the script name in the sorted vector
    // assume "" is the first element
    if (!name)
        return 0;
    ScriptNameMap::const_iterator itr = std::lower_bound(m_scriptNames.begin(), m_scriptNames.end(), name);
    if (itr == m_scriptNames.end() || *itr != name)
        return 0;
    return uint32(itr - m_scriptNames.begin());
}

void ObjectMgr::CheckScripts (ScriptsType type, std::set<int32>& ids)
{
    ScriptMapMap *scripts = GetScriptsMapByType(type);
    if (!scripts)
        return;
    for (ScriptMapMap::const_iterator itrMM = scripts->begin(); itrMM != scripts->end(); ++itrMM)
    {
        for (ScriptMap::const_iterator itrM = itrMM->second.begin(); itrM != itrMM->second.end(); ++itrM)
        {
            switch (itrM->second.command)
            {
            case SCRIPT_COMMAND_TALK:
            {
                if (!GetArkCoreStringLocale(itrM->second.Talk.TextID))
                    sLog->outErrorDb("Table `db_script_string` not has string id  %u used db script (ID: %u)", itrM->second.Talk.TextID, itrMM->first);

                if (ids.find(itrM->second.Talk.TextID) != ids.end())
                    ids.erase(itrM->second.Talk.TextID);

                break;
            }
            default:
                break;
            }
        }
    }
}

void ObjectMgr::LoadDbScriptStrings ()
{
    LoadArkCoreStrings("db_script_string", MIN_DB_SCRIPT_STRING_ID, MAX_DB_SCRIPT_STRING_ID);

    std::set<int32> ids;

    for (int32 i = MIN_DB_SCRIPT_STRING_ID; i < MAX_DB_SCRIPT_STRING_ID; ++i)
        if (GetArkCoreStringLocale(i))
            ids.insert(i);

    for (int type = SCRIPTS_FIRST; type < SCRIPTS_LAST; ++type)
        CheckScripts(ScriptsType(type), ids);

    for (std::set<int32>::const_iterator itr = ids.begin(); itr != ids.end(); ++itr)
        sLog->outErrorDb("Table `db_script_string` has unused string id  %u", *itr);
}

// Functions for scripting access
uint32 GetAreaTriggerScriptId (uint32 trigger_id)
{
    return sObjectMgr->GetAreaTriggerScriptId(trigger_id);
}

bool LoadArkCoreStrings (char const* table, int32 start_value, int32 end_value)
{
    // MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
    // start/end reversed for negative values
    if (start_value > MAX_DB_SCRIPT_STRING_ID || end_value >= start_value)
    {
        sLog->outErrorDb("Table '%s' load attempted with range (%d - %d) reserved by Trinity, strings not loaded.", table, start_value, end_value + 1);
        return false;
    }

    return sObjectMgr->LoadArkCoreStrings(table, start_value, end_value);
}

uint32 GetScriptId (const char *name)
{
    return sObjectMgr->GetScriptId(name);
}

ObjectMgr::ScriptNameMap & GetScriptNames ()
{
    return sObjectMgr->GetScriptNames();
}

GameObjectInfo const *GetGameObjectInfo (uint32 id)
{
    return ObjectMgr::GetGameObjectInfo(id);
}

CreatureInfo const *GetCreatureInfo (uint32 id)
{
    return ObjectMgr::GetCreatureTemplate(id);
}

CreatureInfo const* GetCreatureTemplateStore (uint32 entry)
{
    return sCreatureStorage.LookupEntry<CreatureInfo>(entry);
}

Quest const* GetQuestTemplateStore (uint32 entry)
{
    return sObjectMgr->GetQuestTemplate(entry);
}

CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats (uint8 level, uint8 unitClass)
{
    CreatureBaseStatsMap::const_iterator it = m_creatureBaseStatsMap.find(MAKE_PAIR16(level, unitClass));

    if (it != m_creatureBaseStatsMap.end())
        return &(it->second);

    struct DefaultCreatureBaseStats: public CreatureBaseStats
    {
        DefaultCreatureBaseStats ()
        {
            BaseArmor = 1;
            for (uint8 j = 0; j < MAX_CREATURE_BASE_HP; ++j)
                BaseHealth[j] = 1;
            BaseMana = 0;
        }
    };
    static const DefaultCreatureBaseStats def_stats;
    return &def_stats;
}

void ObjectMgr::LoadCreatureClassLevelStats ()
{
    QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basehp3, basemana, basearmor FROM creature_classlevelstats");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
        return;
    }

    uint32 counter = 0;

    do
    {
        Field *fields = result->Fetch();

        uint8 Level = fields[0].GetUInt8();
        uint8 Class = fields[1].GetUInt8();

        CreatureBaseStats stats;
        for (uint8 i = 0; i < MAX_CREATURE_BASE_HP; ++i)
            stats.BaseHealth[i] = fields[i + 2].GetUInt32();
        stats.BaseMana = fields[6].GetUInt32();
        stats.BaseArmor = fields[7].GetUInt32();

        if (!Class || ((1 << (Class - 1)) & CLASSMASK_ALL_CREATURES) == 0)
            sLog->outErrorDb("Creature base stats for level %u has invalid class %u", Level, Class);

        for (uint8 i = 0; i < MAX_CREATURE_BASE_HP; ++i)
        {
            if (stats.BaseHealth[i] < 1)
            {
                sLog->outErrorDb("Creature base stats for class %u, level %u has invalid zero base HP[%u] - set to 1", Class, Level, i);
                stats.BaseHealth[i] = 1;
            }
        }

        m_creatureBaseStatsMap[MAKE_PAIR16(Level, Class)] = stats;

        ++counter;
    }
    while (result->NextRow());

    for (uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
    {
        CreatureInfo const* info = sCreatureStorage.LookupEntry<CreatureInfo>(i);
        if (!info)
            continue;

        for (uint16 lvl = info->minlevel; lvl <= info->maxlevel; ++lvl)
        {
            if (m_creatureBaseStatsMap.find(MAKE_PAIR16(lvl, info->unit_class)) == m_creatureBaseStatsMap.end())
                sLog->outErrorDb("Missing base stats for creature class %u level %u", info->unit_class, lvl);
        }
    }

    sLog->outString();
    sLog->outString(">> Loaded %u creature base stats.", counter);
}

void ObjectMgr::LoadFactionChangeAchievements ()
{
    QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_achievement");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 faction change achievement pairs. DB table `player_factionchange_achievement` is empty.");
        return;
    }

    uint32 counter = 0;

    do
    {
        Field *fields = result->Fetch();

        uint32 alliance = fields[0].GetUInt32();
        uint32 horde = fields[1].GetUInt32();

        if (!sAchievementStore.LookupEntry(alliance))
            sLog->outErrorDb("Achievement %u referenced in `player_factionchange_achievement` does not exist, pair skipped!", alliance);
        else if (!sAchievementStore.LookupEntry(horde))
            sLog->outErrorDb("Achievement %u referenced in `player_factionchange_achievement` does not exist, pair skipped!", horde);
        else
            factionchange_achievements[alliance] = horde;

        ++counter;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u faction change achievement pairs.", counter);
}

void ObjectMgr::LoadFactionChangeItems ()
{
    QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_items");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 faction change item pairs. DB table `player_factionchange_items` is empty.");
        return;
    }

    uint32 counter = 0;

    do
    {
        Field *fields = result->Fetch();

        uint32 alliance = fields[0].GetUInt32();
        uint32 horde = fields[1].GetUInt32();

        if (!ObjectMgr::GetItemPrototype(alliance))
            sLog->outErrorDb("Item %u referenced in `player_factionchange_items` does not exist, pair skipped!", alliance);
        else if (!ObjectMgr::GetItemPrototype(horde))
            sLog->outErrorDb("Item %u referenced in `player_factionchange_items` does not exist, pair skipped!", horde);
        else
            factionchange_items[alliance] = horde;

        ++counter;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u faction change item pairs.", counter);
}

void ObjectMgr::LoadFactionChangeSpells ()
{
    QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_spells");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 faction change spell pairs. DB table `player_factionchange_spells` is empty.");
        return;
    }

    uint32 counter = 0;

    do
    {
        Field *fields = result->Fetch();

        uint32 alliance = fields[0].GetUInt32();
        uint32 horde = fields[1].GetUInt32();

        if (!sSpellStore.LookupEntry(alliance))
            sLog->outErrorDb("Spell %u referenced in `player_factionchange_spells` does not exist, pair skipped!", alliance);
        else if (!sSpellStore.LookupEntry(horde))
            sLog->outErrorDb("Spell %u referenced in `player_factionchange_spells` does not exist, pair skipped!", horde);
        else
            factionchange_spells[alliance] = horde;

        ++counter;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u faction change spell pairs.", counter);
}

void ObjectMgr::LoadFactionChangeReputations ()
{
    QueryResult result = WorldDatabase.Query("SELECT alliance_id, horde_id FROM player_factionchange_reputations");

    if (!result)
    {
        sLog->outString();
        sLog->outString(">> Loaded 0 faction change reputation pairs. DB table `player_factionchange_reputations` is empty.");
        return;
    }

    uint32 counter = 0;

    do
    {
        Field *fields = result->Fetch();

        uint32 alliance = fields[0].GetUInt32();
        uint32 horde = fields[1].GetUInt32();

        if (!sFactionStore.LookupEntry(alliance))
            sLog->outErrorDb("Reputation %u referenced in `player_factionchange_reputations` does not exist, pair skipped!", alliance);
        else if (!sFactionStore.LookupEntry(horde))
            sLog->outErrorDb("Reputation %u referenced in `player_factionchange_reputations` does not exist, pair skipped!", horde);
        else
            factionchange_reputations[alliance] = horde;

        ++counter;
    }
    while (result->NextRow());

    sLog->outString();
    sLog->outString(">> Loaded %u faction change reputation pairs.", counter);
}
