/*
* Copyright (C) 2005-2009 MaNGOS
*
* Copyright (C) 2008-2010 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
#include "GossipDef.h"
#include "Vehicle.h"
#include "AchievementMgr.h"
INSTANTIATE_SINGLETON_1(ObjectMgr);
ScriptMapMap sQuestEndScripts;
ScriptMapMap sQuestStartScripts;
ScriptMapMap sSpellScripts;
ScriptMapMap sGameObjectScripts;
ScriptMapMap sEventScripts;
ScriptMapMap sGossipScripts;
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 = 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;
}
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_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;
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();
}
Group * ObjectMgr::GetGroupByGUID(const uint64 &guid) const
{
for (GroupSet::const_iterator itr = mGroupSet.begin(); itr != mGroupSet.end(); ++itr)
if ((*itr)->GetGUID() == 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
{
std::string search = guildname;
std::transform(search.begin(), search.end(), search.begin(), toupper);
for (GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
{
std::string gname = itr->second->GetName();
std::transform(gname.begin(), gname.end(), gname.begin(), toupper);
if (search == gname)
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
{
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);
}
CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
{
return sCreatureStorage.LookupEntry(id);
}
void ObjectMgr::LoadCreatureLocales()
{
mCreatureLocaleMap.clear(); // need for reload case
QueryResult_AutoPtr 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;
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());
sLog.outString();
sLog.outString(">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size());
}
void ObjectMgr::LoadNpcOptionLocales()
{
mGossipMenuItemsLocaleMap.clear(); // need for reload case
QueryResult_AutoPtr 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;
barGoLink bar(result->GetRowCount());
do
{
Field *fields = result->Fetch();
bar.step();
uint16 menuId = fields[0].GetUInt16();
uint16 id = fields[1].GetUInt16();
GossipMenuItemsLocale& data = mGossipMenuItemsLocaleMap[MAKE_PAIR32(menuId,id)];
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());
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_AutoPtr 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;
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());
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 difficultyEntries[MAX_DIFFICULTY - 1]; // already loaded difficulty 1 value in creatures
std::set hasDifficultyEntries[MAX_DIFFICULTY - 1]; // already loaded creatures with difficulty 1 values
// check data correctness
for (uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i);
if (!cInfo)
continue;
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) have `difficulty_entry_%u`=%u but creature entry %u not exist.",
i, diff + 1, cInfo->DifficultyEntry[diff], cInfo->DifficultyEntry[diff]);
continue;
}
if (difficultyEntries[diff].find(i) != difficultyEntries[diff].end())
{
sLog.outErrorDb("Creature (Entry: %u) listed as difficulty %u but have value in `difficulty_entry_1`.", i, diff + 1);
continue;
}
bool ok2 = true;
for (uint32 diff2 = 0; diff2 < MAX_DIFFICULTY - 1 && ok2; ++diff2)
{
ok2 = false;
if (difficultyEntries[diff2].find(cInfo->DifficultyEntry[diff]) != difficultyEntries[diff2].end())
{
sLog.outErrorDb("Creature (Entry: %u) already listed as difficulty %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) have `difficulty_entry_%u`=%u but creature entry %u have difficulty %u entry also.",
i, 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).",
i, 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).", i, 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).", i, 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).", i, 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).", i, 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).", i, diff + 1, cInfo->DifficultyEntry[diff]);
continue;
}
if (difficultyInfo->AIName && *difficultyInfo->AIName)
{
sLog.outErrorDb("Difficulty %u mode creature (Entry: %u) has `AIName`, but in any case will used difficulty 0 mode creature (Entry: %u) AIName.",
diff, cInfo->DifficultyEntry[diff], i);
continue;
}
if (difficultyInfo->ScriptID)
{
sLog.outErrorDb("Difficulty %u mode creature (Entry: %u) has `ScriptName`, but in any case will used difficulty 0 mode creature (Entry: %u) ScriptName.",
diff, cInfo->DifficultyEntry[diff], i);
continue;
}
hasDifficultyEntries[diff].insert(i);
difficultyEntries[diff].insert(cInfo->DifficultyEntry[diff]);
ok = true;
}
if (!ok)
continue;
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) has non-existing Modelid1 id (%u), can crash client", cInfo->Entry, cInfo->Modelid1);
const_cast(cInfo)->Modelid1 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->Modelid1);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid1 (%u)", cInfo->Entry, cInfo->Modelid1);
}
if (cInfo->Modelid2)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
if (!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid2 id (%u), can crash client", cInfo->Entry, cInfo->Modelid2);
const_cast(cInfo)->Modelid2 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->Modelid2);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid2 (%u)", cInfo->Entry, cInfo->Modelid2);
}
if (cInfo->Modelid3)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
if (!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid3 id (%u), can crash client", cInfo->Entry, cInfo->Modelid3);
const_cast(cInfo)->Modelid3 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->Modelid3);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid3 (%u)", cInfo->Entry, cInfo->Modelid3);
}
if (cInfo->Modelid4)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
if (!displayEntry)
{
sLog.outErrorDb("Creature (Entry: %u) has non-existing Modelid4 id (%u), can crash client", cInfo->Entry, cInfo->Modelid4);
const_cast(cInfo)->Modelid4 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry(cInfo->Modelid4);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid4 (%u)", cInfo->Entry, cInfo->Modelid4);
}
if (!displayScaleEntry)
sLog.outErrorDb("Creature (Entry: %u) not has any existed 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) 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. Set to 1 (UNIT_CLASS_WARRIOR).", cInfo->Entry, cInfo->unit_class);
const_cast(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(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->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);
}
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;
}
if (cInfo->expansion > (MAX_CREATURE_BASE_HP - 1))
{
sLog.outErrorDb("Table `creature_template` have creature (Entry: %u) with expansion %u ignore and set to NULL.", cInfo->expansion);
const_cast(cInfo)->expansion = 0;
}
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::map val;
s=p=(char*)reinterpret_cast(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<(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(addon->auras) = new CreatureDataAddonAura[val.size()+1];
uint32 i=0;
for (std::map::iterator itr = val.begin(); itr != val.end();++itr)
{
CreatureDataAddonAura& cAura = const_cast(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<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(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(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());
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(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)
uint32 display_id = 0;
if (!data || data->displayid == 0)
{
display_id = cinfo->GetRandomValidModelId();
}
else
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->Modelid1;
case 33218: // Pyrite Safety Container
return cinfo->Modelid2;
case 33143: // Overload Control Device
return cinfo->Modelid3;
default:
return cinfo->GetRandomValidModelId();
}
}*/
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_AutoPtr 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());
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_AutoPtr 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 difficultyCreatures[MAX_DIFFICULTY - 1];
for (uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(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 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
//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();
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;
}
if (data.spawnMask & ~spawnMasks[data.mapid])
sLog.outErrorDb("Table `creature` have creature (GUID: %u) that have wrong spawn mask %u including not supported difficulty modes for map (Id: %u).",guid, data.spawnMask, data.mapid);
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(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 (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());
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