/*
* Copyright (C) 2005-2009 MaNGOS
*
* Copyright (C) 2008-2009 Trinity
*
* 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 "Common.h"
#include "Database/DatabaseEnv.h"
#include "Database/SQLStorage.h"
#include "Database/SQLStorageImpl.h"
#include "Policies/SingletonImp.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 "Transports.h"
#include "ProgressBar.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 "InstanceData.h" //for condition_instance_data
INSTANTIATE_SINGLETON_1(ObjectMgr);
ScriptMapMap sQuestEndScripts;
ScriptMapMap sQuestStartScripts;
ScriptMapMap sSpellScripts;
ScriptMapMap sGameObjectScripts;
ScriptMapMap sEventScripts;
ScriptMapMap sWaypointScripts;
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 }
};
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 = ((TempSummon*)clickNpc)->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;
}
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_ItemTextId = 1;
m_mailid = 1;
m_equipmentSetGuid = 1;
m_guildId = 1;
m_arenaTeamId = 1;
m_auctionid = 1;
// Only zero condition left, others will be added while loading DB tables
mConditions.resize(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)
delete itr->second;
mGuildMap.clear();
for (CachePlayerInfoMap::iterator itr = m_mPlayerInfoMap.begin(); itr != m_mPlayerInfoMap.end(); ++itr)
delete itr->second;
for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
delete itr->second;
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
itr->second.Clear();
for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
itr->second.Clear();
}
void ObjectMgr::LoadPlayerInfoInCache()
{
QueryResult *result = CharacterDatabase.PQuery("SELECT guid, name, data, class FROM characters");
if(!result)
{
sLog.outError( "Loading Player Cache failed.");
return;
}
PCachePlayerInfo pPPlayerInfo = NULL;
Field *fields = NULL;
Tokens tdata;
barGoLink bar( result->GetRowCount() );
do
{
bar.step();
fields = result->Fetch();
pPPlayerInfo = new CachePlayerInfo();
pPPlayerInfo->sPlayerName = fields[1].GetString();
tdata.clear();
tdata = StrSplit(fields[2].GetCppString(), " ");
pPPlayerInfo->unLevel = Player::GetUInt32ValueFromArray(tdata,UNIT_FIELD_LEVEL);
pPPlayerInfo->unfield = Player::GetUInt32ValueFromArray(tdata,UNIT_FIELD_BYTES_0);
pPPlayerInfo->unArenaInfoId0 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 0 * 6);
pPPlayerInfo->unArenaInfoId1 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 * 6);
pPPlayerInfo->unArenaInfoId2 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 2 * 6);
pPPlayerInfo->unArenaInfoSlot0 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 0 * 6 + 5);
pPPlayerInfo->unArenaInfoSlot1 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 * 6 + 5);
pPPlayerInfo->unArenaInfoSlot2 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 2 * 6 + 5);
pPPlayerInfo->unClass = (uint32)fields[3].GetUInt32();
m_mPlayerInfoMap[fields[0].GetUInt32()] = pPPlayerInfo;
}
while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded info about %d players", m_mPlayerInfoMap.size());
}
PCachePlayerInfo ObjectMgr::GetPlayerInfoFromCache(uint32 unPlayerGuid) const
{
//Now m_mPlayerInfoMap is using only for search, but when dinamic inserting/removing
//will be implemented we should lock it to prevent simultaneous access.
//Inserting - when new created player is saving
//Removing - when player has been deleted
CachePlayerInfoMap::const_iterator ipos = m_mPlayerInfoMap.find(unPlayerGuid);
return ipos == m_mPlayerInfoMap.end() ? NULL : ipos->second;
}
Group * ObjectMgr::GetGroupByLeader(const uint64 &guid) const
{
for(GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
if ((*itr)->GetLeaderGUID() == guid)
return *itr;
return NULL;
}
Guild * ObjectMgr::GetGuildById(uint32 GuildId) const
{
GuildMap::const_iterator itr = mGuildMap.find(GuildId);
if (itr != mGuildMap.end())
return itr->second;
return NULL;
}
Guild * ObjectMgr::GetGuildByName(const std::string& guildname) const
{
for(GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
if (itr->second->GetName() == guildname)
return itr->second;
return NULL;
}
std::string ObjectMgr::GetGuildNameById(uint32 GuildId) const
{
GuildMap::const_iterator itr = mGuildMap.find(GuildId);
if (itr != mGuildMap.end())
return itr->second->GetName();
return "";
}
Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
{
for(GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
if (itr->second->GetLeader() == guid)
return itr->second;
return NULL;
}
void ObjectMgr::AddGuild(Guild* guild)
{
mGuildMap[guild->GetId()] = guild;
}
void ObjectMgr::RemoveGuild(uint32 Id)
{
mGuildMap.erase(Id);
}
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
{
for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
if (itr->second->GetName() == arenateamname)
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);
}
CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
{
return sCreatureStorage.LookupEntry(id);
}
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)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 creature locale strings. DB table `locales_creature` is empty.");
return;
}
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
CreatureLocale& data = mCreatureLocaleMap[entry];
for(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
if(!str.empty())
{
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
if(idx >= 0)
{
if(data.Name.size() <= idx)
data.Name.resize(idx+1);
data.Name[idx] = str;
}
}
str = fields[1+2*(i-1)+1].GetCppString();
if(!str.empty())
{
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
if(idx >= 0)
{
if(data.SubName.size() <= idx)
data.SubName.resize(idx+1);
data.SubName[idx] = str;
}
}
}
} while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size() );
}
void ObjectMgr::LoadNpcOptionLocales()
{
mNpcOptionLocaleMap.clear(); // need for reload case
QueryResult *result = WorldDatabase.Query("SELECT entry,"
"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_npc_option");
if(!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty.");
return;
}
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
NpcOptionLocale& data = mNpcOptionLocaleMap[entry];
for(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
if(!str.empty())
{
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
if(idx >= 0)
{
if(data.OptionText.size() <= idx)
data.OptionText.resize(idx+1);
data.OptionText[idx] = str;
}
}
str = fields[1+2*(i-1)+1].GetCppString();
if(!str.empty())
{
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
if(idx >= 0)
{
if(data.BoxText.size() <= idx)
data.BoxText.resize(idx+1);
data.BoxText[idx] = str;
}
}
}
} while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.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)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty.");
return;
}
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry];
for(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[i].GetCppString();
if(str.empty())
continue;
int idx = GetOrNewIndexForLocale(LocaleConstant(i));
if(idx >= 0)
{
if(data.IconName.size() <= idx)
data.IconName.resize(idx+1);
data.IconName[idx] = str;
}
}
} while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %lu points_of_interest locale strings", (unsigned long)mPointOfInterestLocaleMap.size() );
}
struct SQLCreatureLoader : public SQLStorageLoaderBase
{
template
void convert_from_str(uint32 /*field_pos*/, char *src, D &dst)
{
dst = D(objmgr.GetScriptId(src));
}
};
void ObjectMgr::LoadCreatureTemplates()
{
SQLCreatureLoader loader;
loader.Load(sCreatureStorage);
sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
sLog.outString();
std::set heroicEntries; // already loaded heroic value in creatures
std::set hasHeroicEntries; // already loaded creatures with heroic entry values
// check data correctness
for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i);
if (!cInfo)
continue;
if (cInfo->HeroicEntry)
{
CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
if (!heroicInfo)
{
sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.", i, cInfo->HeroicEntry, cInfo->HeroicEntry);
continue;
}
if (heroicEntries.find(i)!=heroicEntries.end())
{
sLog.outErrorDb("Creature (Entry: %u) listed as heroic but have value in `heroic_entry`.",i);
continue;
}
if (heroicEntries.find(cInfo->HeroicEntry)!=heroicEntries.end())
{
sLog.outErrorDb("Creature (Entry: %u) already listed as heroic for another entry.",cInfo->HeroicEntry);
continue;
}
if (hasHeroicEntries.find(cInfo->HeroicEntry)!=hasHeroicEntries.end())
{
sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u have heroic entry also.",i,cInfo->HeroicEntry,cInfo->HeroicEntry);
continue;
}
if (cInfo->unit_class != heroicInfo->unit_class)
{
sLog.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in heroic mode (Entry: %u, class %u).",i, cInfo->unit_class, cInfo->HeroicEntry, heroicInfo->unit_class);
continue;
}
if (cInfo->npcflag != heroicInfo->npcflag)
{
sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `npcflag` in heroic mode.",i);
continue;
}
if (cInfo->trainer_class != heroicInfo->trainer_class)
{
sLog.outErrorDb("Creature (Entry: %u) has different `trainer_class` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
continue;
}
if (cInfo->trainer_race != heroicInfo->trainer_race)
{
sLog.outErrorDb("Creature (Entry: %u) has different `trainer_race` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
continue;
}
if (cInfo->trainer_type != heroicInfo->trainer_type)
{
sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_type` in heroic mode.",i);
continue;
}
if (cInfo->trainer_spell != heroicInfo->trainer_spell)
{
sLog.outErrorDb("Creature (Entry: %u) listed in `creature_template_substitution` has different `trainer_spell` in heroic mode.",i);
continue;
}
if (heroicInfo->AIName && *heroicInfo->AIName)
{
sLog.outErrorDb("Heroic mode creature (Entry: %u) has `AIName`, but in any case will used normal mode creature (Entry: %u) AIName.",cInfo->HeroicEntry,i);
continue;
}
if (heroicInfo->ScriptID)
{
sLog.outErrorDb("Heroic mode creature (Entry: %u) has `ScriptName`, but in any case will used normal mode creature (Entry: %u) ScriptName.",cInfo->HeroicEntry,i);
continue;
}
hasHeroicEntries.insert(i);
heroicEntries.insert(cInfo->HeroicEntry);
}
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->DisplayID_A[0])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[0]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_A id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[0]);
const_cast(cInfo)->DisplayID_A[0] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->DisplayID_A[0]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_A (%u)", cInfo->Entry, cInfo->DisplayID_A[0]);
}
if (cInfo->DisplayID_A[1])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_A[1]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_A2 id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_A[1]);
const_cast(cInfo)->DisplayID_A[1] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->DisplayID_A[1]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_A2 (%u)", cInfo->Entry, cInfo->DisplayID_A[1]);
}
if (cInfo->DisplayID_H[0])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[0]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_H id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[0]);
const_cast(cInfo)->DisplayID_H[0] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->DisplayID_H[0]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_H (%u)", cInfo->Entry, cInfo->DisplayID_H[0]);
}
if (cInfo->DisplayID_H[1])
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->DisplayID_H[1]);
if(!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing DisplayID_H2 id (%u), can crash client", cInfo->Entry, cInfo->DisplayID_H[1]);
const_cast(cInfo)->DisplayID_H[1] = 0;
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->DisplayID_H[1]);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for DisplayID_H2 (%u)", cInfo->Entry, cInfo->DisplayID_H[1]);
}
if (!displayScaleEntry)
sLog.outErrorDb("Creature (Entry: %u) not has any existed display id in DisplayID_A/DisplayID_A2/DisplayID_H/DisplayID_H2", 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) has not existed creature entry in `KillCredit%d` (%u)",cInfo->Entry,k+1,cInfo->KillCredit[k]);
const_cast(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) for creature_template", cInfo->Entry, cInfo->unit_class);
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(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
}
if(cInfo->baseattacktime == 0)
const_cast(cInfo)->baseattacktime = BASE_ATTACK_TIME;
if(cInfo->rangeattacktime == 0)
const_cast(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 expect to be set by code base at `npc_spellclick_spells` content.",cInfo->Entry,UNIT_NPC_FLAG_SPELLCLICK);
const_cast(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(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(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(cInfo)->InhabitType = INHABIT_ANYWHERE;
}
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(cInfo)->spells[j] = 0;
}
}
if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
{
sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
const_cast(cInfo)->MovementType = IDLE_MOTION_TYPE;
}
if(cInfo->equipmentId > 0) // 0 no equipment
{
if(!GetEquipmentInfo(cInfo->equipmentId))
{
sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId);
const_cast(cInfo)->equipmentId = 0;
}
}
/// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
if(cInfo->scale <= 0.0f)
{
if(displayScaleEntry)
const_cast(cInfo)->scale = displayScaleEntry->scale;
else
const_cast(cInfo)->scale = 1.0f;
}
const_cast(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::vector val;
s=p=(char*)reinterpret_cast(addon->auras);
if(p)
{
while (p[0]!=0)
{
++p;
if (p[0]==' ')
{
val.push_back(atoi(s));
s=++p;
}
}
if (p!=s)
val.push_back(atoi(s));
// free char* loaded memory
delete[] (char*)reinterpret_cast(addon->auras);
// wrong list
if (val.size()%2)
{
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(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
int i=0;
for(uint32 j=0;j(addon->auras[i]);
cAura.spell_id = (uint32)val[2*j+0];
cAura.effect_idx = (uint32)val[2*j+1];
if ( cAura.effect_idx > 2 )
{
sLog.outErrorDb("Creature (%s: %u) has wrong effect %u for spell %u in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
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;
}
if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
{
sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
continue;
}
++i;
}
// fill terminator element (after last added)
CreatureDataAddonAura& endAura = const_cast(addon->auras[i]);
endAura.spell_id = 0;
endAura.effect_idx = 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(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(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(addon)->move_flags &= ~(MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4);
}*/
ConvertCreatureAddonAuras(const_cast(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(i))
if(!sCreatureStorage.LookupEntry(addon->guidOrEntry))
sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`",addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName());
LoadCreatureAddons(sCreatureDataAddonStorage,"GUID","creature addons");
// check entry ids
for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
if(CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry(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(entry);
}
void ObjectMgr::LoadEquipmentTemplates()
{
sEquipmentStorage.Load();
for(uint32 i=0; i< sEquipmentStorage.MaxEntry; ++i)
{
EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry(i);
if(!eqInfo)
continue;
for(uint8 j=0; j<3; j++)
{
if(!eqInfo->equipentry[j])
continue;
ItemEntry const *dbcitem = sItemStore.LookupEntry(eqInfo->equipentry[j]);
if(!dbcitem)
{
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(eqInfo)->equipentry[j] = 0;
continue;
}
if(dbcitem->InventoryType != INVTYPE_WEAPON &&
dbcitem->InventoryType != INVTYPE_SHIELD &&
dbcitem->InventoryType != INVTYPE_RANGED &&
dbcitem->InventoryType != INVTYPE_2HWEAPON &&
dbcitem->InventoryType != INVTYPE_WEAPONMAINHAND &&
dbcitem->InventoryType != INVTYPE_WEAPONOFFHAND &&
dbcitem->InventoryType != INVTYPE_HOLDABLE &&
dbcitem->InventoryType != INVTYPE_THROWN &&
dbcitem->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(eqInfo)->equipentry[j] = 0;
}
}
}
sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
sLog.outString();
}
CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
{
return sCreatureModelStorage.LookupEntry(modelid);
}
uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data /*= NULL*/)
{
// Load creature model (display id)
if (data && data->displayid)
return data->displayid;
if(!team)
{
switch(cinfo->Entry)
{
case 28511: // Eye of Acherus
case 33114: // Flame Leviathan Seat (model 24914 chair)
case 33167: // Salvaged Demolisher Mechanic Seat
case 33189: // Liquid Pryite
return cinfo->DisplayID_A[0];
case 33218: // Pyrite Safety Container
return cinfo->DisplayID_A[1];
case 33143: // Overload Control Device
return cinfo->DisplayID_H[0];
default:
return cinfo->GetRandomValidModelId();
}
}
// use defaults from the template
uint32 display_id;
// DisplayID_A is used if no team is given
if (team == HORDE)
{
if(cinfo->DisplayID_H[0])
display_id = cinfo->DisplayID_H[1] ? cinfo->DisplayID_H[urand(0,1)] : cinfo->DisplayID_H[0];
else
display_id = cinfo->DisplayID_H[1];
if(!display_id)
display_id = cinfo->DisplayID_A[0] ? cinfo->DisplayID_A[0] : cinfo->DisplayID_A[1];
}
else if(team == ALLIANCE)
{
if(cinfo->DisplayID_A[0])
display_id = cinfo->DisplayID_A[1] ? cinfo->DisplayID_A[urand(0,1)] : cinfo->DisplayID_A[0];
else
display_id = cinfo->DisplayID_A[1];
if(!display_id)
display_id = cinfo->DisplayID_H[0] ? cinfo->DisplayID_H[0] : cinfo->DisplayID_H[1];
}
return display_id;
}
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(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(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(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(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(mInfo)->combat_reach = DEFAULT_COMBAT_REACH;
}
}
}
bool ObjectMgr::CheckCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid) const
{
const CreatureData* const slave = GetCreatureData(guid);
const CreatureData* const master = GetCreatureData(linkedGuid);
if(!slave || !master) // they must have a corresponding entry in db
{
sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' which doesn't exist",guid,linkedGuid);
return false;
}
const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
if(master->mapid != slave->mapid // link only to same map
&& (!map || map->Instanceable())) // or to unistanced world
{
sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' on an unpermitted map",guid,linkedGuid);
return false;
}
if(!(master->spawnMask & slave->spawnMask) // they must have a possibility to meet (normal/heroic difficulty)
&& (!map || map->Instanceable()))
{
sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask",guid,linkedGuid);
return false;
}
return true;
}
void ObjectMgr::LoadCreatureLinkedRespawn()
{
mCreatureLinkedRespawnMap.clear();
QueryResult *result = WorldDatabase.Query("SELECT guid, linkedGuid FROM creature_linked_respawn ORDER BY guid ASC");
if(!result)
{
barGoLink bar(1);
bar.step();
sLog.outString("");
sLog.outErrorDb(">> Loaded 0 linked respawns. DB table `creature_linked_respawn` is empty.");
return;
}
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint32 guid = fields[0].GetUInt32();
uint32 linkedGuid = fields[1].GetUInt32();
if(CheckCreatureLinkedRespawn(guid,linkedGuid))
mCreatureLinkedRespawnMap[guid] = linkedGuid;
} while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %u linked respawns", mCreatureLinkedRespawnMap.size() );
}
bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid)
{
if(!guid)
return false;
if(!linkedGuid) // we're removing the linking
{
mCreatureLinkedRespawnMap.erase(guid);
WorldDatabase.DirectPExecute("DELETE FROM creature_linked_respawn WHERE guid = '%u'",guid);
return true;
}
if(CheckCreatureLinkedRespawn(guid,linkedGuid)) // we add/change linking
{
mCreatureLinkedRespawnMap[guid] = linkedGuid;
WorldDatabase.DirectPExecute("REPLACE INTO creature_linked_respawn (guid,linkedGuid) VALUES ('%u','%u')",guid,linkedGuid);
return true;
}
return false;
}
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 "
"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)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
return;
}
// build single time for check creature data
std::set heroicCreatures;
for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i))
if(cInfo->HeroicEntry)
heroicCreatures.insert(cInfo->HeroicEntry);
//TODO: remove this
//gameeventmgr.mGameEventCreatureGuids.resize(52*2-1);
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
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();
int16 PoolId = fields[19].GetInt16();
if(heroicCreatures.find(data.id)!=heroicCreatures.end())
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template in `creature_template_substitution`, skipped.",guid,data.id );
continue;
}
// I do not know why but in db most display id are not zero
/*if(data.displayid == 11686 || data.displayid == 24719)
{
(const_cast(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->RegenHealth && data.curhealth < cInfo->minhealth)
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.",guid,data.id,data.curhealth, cInfo->minhealth );
data.curhealth = cInfo->minhealth;
}
if(cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
{
MapEntry const* map = sMapStore.LookupEntry(data.mapid);
if(!map || !map->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.curmana < cInfo->minmana)
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana );
data.curmana = cInfo->minmana;
}
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(entry == 32307 || entry == 32308)
/*if(entry == 30739 || entry == 30740)
{
gameEvent = 51;
uint32 guid2 = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
CreatureData& data2 = mCreatureDataMap[guid2];
data2 = data;
// data2.id = (entry == 32307 ? 32308 : 32307);
data2.id = (entry == 30739 ? 30740 : 30739);
data2.displayid = 0;
gameeventmgr.mGameEventCreatureGuids[51+51].push_back(guid);
gameeventmgr.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());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %lu creatures", (unsigned long)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