/*
* Copyright (C) 2008-2011 TrinityCore
* Copyright (C) 2005-2009 MaNGOS
*
* 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, see .
*/
#include "Common.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "MapManager.h"
#include "ObjectMgr.h"
#include "ArenaTeamMgr.h"
#include "GuildMgr.h"
#include "GroupMgr.h"
#include "SpellMgr.h"
#include "UpdateMask.h"
#include "World.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"
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 }
};
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(Unit const* clicker, Unit const* clickee) const
{
Player const* playerClicker = clicker->ToPlayer();
if (playerClicker)
{
if (questStart)
{
// not in expected required quest state
if (((!questStartCanActive || !playerClicker->IsActiveQuest(questStart)) && !playerClicker->GetQuestRewardStatus(questStart)))
return false;
}
if (questEnd)
{
// not in expected forbidden quest state
if (playerClicker->GetQuestRewardStatus(questEnd))
return false;
}
}
if (auraRequired)
if (!clicker->HasAura(auraRequired))
return false;
if (auraForbidden)
if (clicker->HasAura(auraForbidden))
return false;
Unit const* summoner = NULL;
// Check summoners for party
if (clickee->isSummon())
summoner = clickee->ToTempSummon()->GetSummoner();
if (!summoner)
summoner = clickee;
if (!playerClicker)
return true;
// This only applies to players
switch (userType)
{
case SPELL_CLICK_USER_FRIEND:
if (!playerClicker->IsFriendlyTo(summoner))
return false;
break;
case SPELL_CLICK_USER_RAID:
if (!playerClicker->IsInRaidWith(summoner))
return false;
break;
case SPELL_CLICK_USER_PARTY:
if (!playerClicker->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_hiMoTransGuid = 1;
m_ItemTextId = 1;
m_mailid = 1;
m_equipmentSetGuid = 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;
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
itr->second.Clear();
m_mCacheTrainerSpellMap.clear();
}
void ObjectMgr::AddLocaleString(std::string& s, LocaleConstant locale, StringVector& data)
{
if (!s.empty())
{
if (data.size() <= size_t(locale))
data.resize(locale + 1);
data[locale] = s;
}
}
void ObjectMgr::LoadCreatureLocales()
{
uint32 oldMSTime = getMSTime();
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(">> Loaded %lu creature locale strings in %u ms", (unsigned long)mCreatureLocaleMap.size(), GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void ObjectMgr::LoadGossipMenuItemsLocales()
{
uint32 oldMSTime = getMSTime();
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(">> Loaded %lu gossip_menu_option locale strings in %u ms", (unsigned long)mGossipMenuItemsLocaleMap.size(), GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void ObjectMgr::LoadPointOfInterestLocales()
{
uint32 oldMSTime = getMSTime();
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(">> Loaded %lu points_of_interest locale strings in %u ms", (unsigned long)mPointOfInterestLocaleMap.size(), GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void ObjectMgr::LoadCreatureTemplates()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6 7 8
QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, "
// 9 10 11 12 13 14 15 16 17 18 19 20 21
"modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, "
// 22 23 24 25 26 27 28 29 30 31 32
"scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, "
// 33 34 35 36 37 38 39 40 41 42
"dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, "
// 43 44 45 46 47 48 49 50 51 52 53
"type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, "
// 54 55 56 57 58 59 60 61 62 63 64 65 66
"spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, "
// 67 68 69 70 71 72 73 74 75 76 77
"InhabitType, Health_mod, Mana_mod, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, "
// 78 79 80 81 82 83
"movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName "
"FROM creature_template;");
if (!result)
{
sLog->outString(">> Loaded 0 creature template definitions. DB table `creature_template` is empty.");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
CreatureTemplate& creatureTemplate = CreatureTemplateStore[entry];
creatureTemplate.Entry = entry;
for (uint8 i = 0; i < MAX_DIFFICULTY - 1; ++i)
{
creatureTemplate.DifficultyEntry[i] = fields[1 + i].GetUInt32();
}
for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i)
{
creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32();
}
creatureTemplate.Modelid1 = fields[6].GetUInt32();
creatureTemplate.Modelid2 = fields[7].GetUInt32();
creatureTemplate.Modelid3 = fields[8].GetUInt32();
creatureTemplate.Modelid4 = fields[9].GetUInt32();
creatureTemplate.Name = fields[10].GetString();
creatureTemplate.SubName = fields[11].GetString();
creatureTemplate.IconName = fields[12].GetString();
creatureTemplate.GossipMenuId = fields[13].GetUInt32();
creatureTemplate.minlevel = fields[14].GetUInt8();
creatureTemplate.maxlevel = fields[15].GetUInt8();
creatureTemplate.expansion = uint32(fields[16].GetUInt16());
creatureTemplate.faction_A = uint32(fields[17].GetUInt16());
creatureTemplate.faction_H = uint32(fields[18].GetUInt16());
creatureTemplate.npcflag = fields[19].GetUInt32();
creatureTemplate.speed_walk = fields[20].GetFloat();
creatureTemplate.speed_run = fields[21].GetFloat();
creatureTemplate.scale = fields[22].GetFloat();
creatureTemplate.rank = uint32(fields[23].GetUInt8());
creatureTemplate.mindmg = fields[24].GetFloat();
creatureTemplate.maxdmg = fields[25].GetFloat();
creatureTemplate.dmgschool = uint32(fields[26].GetInt8());
creatureTemplate.attackpower = fields[27].GetUInt32();
creatureTemplate.dmg_multiplier = fields[28].GetFloat();
creatureTemplate.baseattacktime = fields[29].GetUInt32();
creatureTemplate.rangeattacktime = fields[30].GetUInt32();
creatureTemplate.unit_class = uint32(fields[31].GetUInt8());
creatureTemplate.unit_flags = fields[32].GetUInt32();
creatureTemplate.dynamicflags = fields[33].GetUInt32();
creatureTemplate.family = uint32(fields[34].GetUInt8());
creatureTemplate.trainer_type = uint32(fields[35].GetUInt8());
creatureTemplate.trainer_spell = fields[36].GetUInt32();
creatureTemplate.trainer_class = uint32(fields[37].GetUInt8());
creatureTemplate.trainer_race = uint32(fields[38].GetUInt8());
creatureTemplate.minrangedmg = fields[39].GetFloat();
creatureTemplate.maxrangedmg = fields[40].GetFloat();
creatureTemplate.rangedattackpower = uint32(fields[41].GetUInt16());
creatureTemplate.type = uint32(fields[42].GetUInt8());
creatureTemplate.type_flags = fields[43].GetUInt32();
creatureTemplate.lootid = fields[44].GetUInt32();
creatureTemplate.pickpocketLootId = fields[45].GetUInt32();
creatureTemplate.SkinLootId = fields[46].GetUInt32();
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
{
creatureTemplate.resistance[i] = fields[47 + i -1].GetInt32();
}
for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
{
creatureTemplate.spells[i] = fields[53 + i].GetUInt32();
}
creatureTemplate.PetSpellDataId = fields[61].GetUInt32();
creatureTemplate.VehicleId = fields[62].GetUInt32();
creatureTemplate.mingold = fields[63].GetUInt32();
creatureTemplate.maxgold = fields[64].GetUInt32();
creatureTemplate.AIName = fields[65].GetString();
creatureTemplate.MovementType = uint32(fields[66].GetUInt8());
creatureTemplate.InhabitType = uint32(fields[67].GetUInt8());
creatureTemplate.ModHealth = fields[68].GetFloat();
creatureTemplate.ModMana = fields[69].GetFloat();
creatureTemplate.ModArmor = fields[70].GetFloat();
creatureTemplate.RacialLeader = fields[71].GetBool();
for (uint8 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i)
{
creatureTemplate.questItems[i] = fields[72 + i].GetUInt32();
}
creatureTemplate.movementId = fields[78].GetUInt32();
creatureTemplate.RegenHealth = fields[79].GetBool();
creatureTemplate.equipmentId = fields[80].GetUInt32();
creatureTemplate.MechanicImmuneMask = fields[81].GetUInt32();
creatureTemplate.flags_extra = fields[82].GetUInt32();
creatureTemplate.ScriptID = GetScriptId(fields[83].GetCString());
++count;
}
while (result->NextRow());
// Checking needs to be done after loading because of the difficulty self referencing
for (CreatureTemplateContainer::const_iterator itr = CreatureTemplateStore.begin(); itr != CreatureTemplateStore.end(); ++itr)
{
CheckCreatureTemplate(&itr->second);
}
sLog->outString(">> Loaded %u creature definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void ObjectMgr::LoadCreatureTemplateAddons()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6
QueryResult result = WorldDatabase.Query("SELECT entry, path_id, mount, bytes1, bytes2, emote, auras FROM creature_template_addon");
if (!result)
{
sLog->outString(">> Loaded 0 creature template addon definitions. DB table `creature_addon` is empty.");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
if (!sObjectMgr->GetCreatureTemplate(entry))
{
sLog->outErrorDb("Creature template (Entry: %u) does not exist but has a record in `creature_template_addon`", entry);
continue;
}
CreatureAddon& creatureAddon = CreatureTemplateAddonStore[entry];
creatureAddon.path_id = fields[1].GetUInt32();
creatureAddon.mount = fields[2].GetUInt32();
creatureAddon.bytes1 = fields[3].GetUInt32();
creatureAddon.bytes2 = fields[4].GetUInt32();
creatureAddon.emote = fields[5].GetUInt32();
Tokens tokens(fields[6].GetString(), ' ');
uint8 i = 0;
creatureAddon.auras.resize(tokens.size());
for (Tokens::iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
{
SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(uint32(atol(*itr)));
if (!AdditionalSpellInfo)
{
sLog->outErrorDb("Creature (GUID: %u) has wrong spell %u defined in `auras` field in `creature_addon`.", entry, uint32(atol(*itr)));
continue;
}
creatureAddon.auras[i++] = uint32(atol(*itr));
}
if (creatureAddon.mount)
{
if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
{
sLog->outErrorDb("Creature (GUID: %u) has invalid displayInfoId (%u) for mount defined in `creature_addon`", entry, creatureAddon.mount);
creatureAddon.mount = 0;
}
}
if (!sEmotesStore.LookupEntry(creatureAddon.emote))
sLog->outErrorDb("Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", entry, creatureAddon.emote);
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u creature template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void ObjectMgr::CheckCreatureTemplate(CreatureTemplate 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
CreatureTemplate 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.empty())
{
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(cInfo)->Modelid1 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1);
if (!modelInfo)
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(cInfo)->Modelid2 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2);;
if (!modelInfo)
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(cInfo)->Modelid3 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3);
if (!modelInfo)
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(cInfo)->Modelid4 = 0;
}
else if (!displayScaleEntry)
displayScaleEntry = displayEntry;
CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4);;
if (!modelInfo)
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(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(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_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);
const_cast(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(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(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(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` lists creature (Entry: %u) with expansion %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion);
const_cast(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(cInfo)->flags_extra &= CREATURE_FLAG_EXTRA_DB_ALLOWED;
}
const_cast(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank);
}
void ObjectMgr::LoadCreatureAddons()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6
QueryResult result = WorldDatabase.Query("SELECT guid, path_id, mount, bytes1, bytes2, emote, auras FROM creature_addon");
if (!result)
{
sLog->outString(">> Loaded 0 creature addon definitions. DB table `creature_addon` is empty.");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field *fields = result->Fetch();
uint32 guid = fields[0].GetUInt32();
if (mCreatureDataMap.find(guid) == mCreatureDataMap.end())
{
sLog->outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`", guid);
continue;
}
CreatureAddon& creatureAddon = CreatureAddonStore[guid];
creatureAddon.path_id = fields[1].GetUInt32();
creatureAddon.mount = fields[2].GetUInt32();
creatureAddon.bytes1 = fields[3].GetUInt32();
creatureAddon.bytes2 = fields[4].GetUInt32();
creatureAddon.emote = fields[5].GetUInt32();
Tokens tokens(fields[6].GetString(), ' ');
uint8 i = 0;
creatureAddon.auras.resize(tokens.size());
for (Tokens::iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
{
SpellEntry const *AdditionalSpellInfo = sSpellStore.LookupEntry(uint32(atol(*itr)));
if (!AdditionalSpellInfo)
{
sLog->outErrorDb("Creature (GUID: %u) has wrong spell %u defined in `auras` field in `creature_addon`.", guid, uint32(atol(*itr)));
continue;
}
creatureAddon.auras[i++] = uint32(atol(*itr));
}
if (creatureAddon.mount)
{
if (!sCreatureDisplayInfoStore.LookupEntry(creatureAddon.mount))
{
sLog->outErrorDb("Creature (GUID: %u) has invalid displayInfoId (%u) for mount defined in `creature_addon`", guid, creatureAddon.mount);
creatureAddon.mount = 0;
}
}
if (!sEmotesStore.LookupEntry(creatureAddon.emote))
sLog->outErrorDb("Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", guid, creatureAddon.emote);
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u creature addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
CreatureAddon const* ObjectMgr::GetCreatureAddon(uint32 lowguid)
{
CreatureAddonContainer::const_iterator itr = CreatureAddonStore.find(lowguid);
if (itr != CreatureAddonStore.end())
return &(itr->second);
return NULL;
}
CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry)
{
CreatureAddonContainer::const_iterator itr = CreatureTemplateAddonStore.find(entry);
if (itr != CreatureTemplateAddonStore.end())
return &(itr->second);
return NULL;
}
EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
{
EquipmentInfoContainer::const_iterator itr = EquipmentInfoStore.find(entry);
if (itr != EquipmentInfoStore.end())
return &(itr->second);
return NULL;
}
void ObjectMgr::LoadEquipmentTemplates()
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.Query("SELECT entry, itemEntry1, itemEntry2, itemEntry3 FROM creature_equip_template");
if (!result)
{
sLog->outString(">> Loaded 0 creature equipment templates. DB table `creature_equip_template` is empty!");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint16 entry = fields[0].GetUInt16();
EquipmentInfo& equipmentInfo = EquipmentInfoStore[entry];
equipmentInfo.ItemEntry[0] = fields[1].GetUInt32();
equipmentInfo.ItemEntry[1] = fields[2].GetUInt32();
equipmentInfo.ItemEntry[2] = fields[3].GetUInt32();
for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i)
{
if (!equipmentInfo.ItemEntry[i])
continue;
ItemEntry const *dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]);
if (!dbcItem)
{
sLog->outErrorDb("Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u, forced to 0.",
equipmentInfo.ItemEntry[i], i+1, entry);
equipmentInfo.ItemEntry[i] = 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.itemEntry%u for entry = %u is not equipable in a hand, forced to 0.",
equipmentInfo.ItemEntry[i], i+1, entry);
equipmentInfo.ItemEntry[i] = 0;
}
}
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u equipment templates in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId)
{
CreatureModelContainer::const_iterator itr = CreatureModelStore.find(modelId);
if (itr != CreatureModelStore.end())
return &(itr->second);
return NULL;
}
uint32 ObjectMgr::ChooseDisplayId(uint32 /*team*/, const CreatureTemplate *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 CreatureTemplate *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* displayID)
{
CreatureModelInfo const* minfo = GetCreatureModelInfo(*displayID);
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`. ", *displayID, minfo->modelid_other_gender);
else
{
// Model ID changed
*displayID = minfo->modelid_other_gender;
return minfo_tmp;
}
}
return minfo;
}
void ObjectMgr::LoadCreatureModelInfo()
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.Query("SELECT modelid, bounding_radius, combat_reach, gender, modelid_other_gender FROM creature_model_info");
if (!result)
{
sLog->outString(">> Loaded 0 creature model definitions. DB table `creature_model_info` is empty.");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field *fields = result->Fetch();
uint32 modelId = fields[0].GetUInt32();
CreatureModelInfo& modelInfo = CreatureModelStore[modelId];
modelInfo.bounding_radius = fields[1].GetFloat();
modelInfo.combat_reach = fields[2].GetFloat();
modelInfo.gender = fields[3].GetUInt8();
modelInfo.modelid_other_gender = fields[4].GetUInt32();
// Checks
if (!sCreatureDisplayInfoStore.LookupEntry(modelId))
sLog->outErrorDb("Table `creature_model_info` has model for not existed display id (%u).", modelId);
if (modelInfo.gender > GENDER_NONE)
{
sLog->outErrorDb("Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(modelInfo.gender), modelId);
modelInfo.gender = GENDER_MALE;
}
if (modelInfo.modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(modelInfo.modelid_other_gender))
{
sLog->outErrorDb("Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", modelInfo.modelid_other_gender, modelId);
modelInfo.modelid_other_gender = 0;
}
if (modelInfo.combat_reach < 0.1f)
{
modelInfo.combat_reach = DEFAULT_COMBAT_REACH;
}
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u creature model based info in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
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 = 0, linkedGuid = 0;
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 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6 7 8 9 10
QueryResult result = WorldDatabase.Query("SELECT creature.guid, id, map, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, "
// 11 12 13 14 15 16 17 18 19 20 21 22
"currentwaypoint, curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, eventEntry, pool_entry, 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->outErrorDb(">> Loaded 0 creatures. DB table `creature` is empty.");
sLog->outString();
return;
}
// Build single time for check creature data
std::set difficultyCreatures[MAX_DIFFICULTY - 1];
CreatureTemplateContainer const* ctc = sObjectMgr->GetCreatureTemplates();
for (CreatureTemplateContainer::const_iterator itr = ctc->begin(); itr != ctc->end(); ++itr)
for (uint32 diff = 0; diff < MAX_DIFFICULTY - 1; ++diff)
if (itr->second.DifficultyEntry[diff])
difficultyCreatures[diff].insert(itr->second.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);
uint32 count = 0;
do
{
Field *fields = result->Fetch();
uint32 guid = fields[ 0].GetUInt32();
uint32 entry = fields[ 1].GetUInt32();
CreatureTemplate 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;
}
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;
// -1 no equipment, 0 use default
if (data.equipmentId > 0)
{
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 (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;
}
// Add to grid if not managed by the game event or pool system
if (gameEvent == 0 && PoolId == 0)
AddCreatureToGrid(guid, &data);
++count;
} while (result->NextRow());
sLog->outString(">> Loaded %u creatures in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
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)
{
GameObjectTemplate const* goinfo = GetGameObjectTemplate(entry);
if (!goinfo)
return 0;
Map* map = const_cast