Files
AscEmu/src/world/Management/ObjectMgr.cpp
2023-07-22 00:17:31 +02:00

2850 lines
96 KiB
C++

/*
* AscEmu Framework based on ArcEmu MMORPG Server
* Copyright (c) 2014-2023 AscEmu Team <http://www.ascemu.org>
* Copyright (C) 2008-2012 ArcEmu Team <http://www.ArcEmu.org/>
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <utility>
#include "Storage/DBC/DBCStores.h"
#include "Management/QuestLogEntry.hpp"
#include "Objects/Container.hpp"
#include "Objects/Units/Stats.h"
#include "Management/ArenaTeam.hpp"
#include "Storage/MySQLDataStore.hpp"
#include "Storage/MySQLStructures.h"
#include "Objects/Units/Players/PlayerClasses.hpp"
#include "Server/MainServerDefines.h"
#include "Map/Maps/InstanceDefines.hpp"
#include "Map/Management/MapMgr.hpp"
#include "Map/Maps/MapScriptInterface.h"
#include "Spell/SpellMgr.hpp"
#include "Objects/Units/Creatures/Pet.h"
#include "Spell/Definitions/SpellEffects.hpp"
#include "Management/TaxiMgr.h"
#include "Management/LFG/LFGMgr.hpp"
#include "Movement/MovementManager.h"
#include "Objects/Units/Creatures/Summons/Summon.hpp"
#include "Utilities/Strings.hpp"
#if VERSION_STRING < Cata
#include "Management/Guild/Guild.hpp"
#endif
const char* NormalTalkMessage = "DMSG";
ObjectMgr& ObjectMgr::getInstance()
{
static ObjectMgr mInstance;
return mInstance;
}
void ObjectMgr::initialize()
{
m_hiItemGuid = 0;
m_hiGroupId = 0;
m_mailid = 0;
m_reportID = 0;
m_setGUID = 0;
m_hiCorpseGuid = 0;
m_hiGuildId = 0;
m_hiPetGuid = 0;
m_hiArenaTeamId = 0;
m_hiPlayerGuid = 1;
#if VERSION_STRING > WotLK
m_voidItemId = 1;
#endif
loadCreatureDisplayInfo();
}
void ObjectMgr::finalize()
{
sLogger.info("ObjectMgr : Deleting Corpses...");
unloadCorpseCollector();
sLogger.info("ObjectMgr : Deleting Vendors...");
for (VendorMap::iterator i = mVendors.begin(); i != mVendors.end(); ++i)
{
delete i->second;
}
sLogger.info("ObjectMgr : Deleting TrainserSpellSets...");
for (auto i = m_trainerSpellSet.begin(); i != m_trainerSpellSet.end(); ++i)
{
delete i->second;
}
sLogger.info("ObjectMgr : Deleting Trainers UIMessages...");
for (auto trainerPair : m_trainers)
{
auto trainer = trainerPair.second;
if (trainer->UIMessage && trainer->UIMessage != (char*)NormalTalkMessage)
delete[] trainer->UIMessage;
}
m_trainers.clear();
sLogger.info("ObjectMgr : Deleting Level Information...");
for (LevelInfoMap::iterator i = mLevelInfo.begin(); i != mLevelInfo.end(); ++i)
{
LevelMap* l = i->second;
for (LevelMap::iterator i2 = l->begin(); i2 != l->end(); ++i2)
{
delete i2->second;
}
l->clear();
delete l;
}
sLogger.info("ObjectMgr : Deleting timed emote Cache...");
for (std::unordered_map<uint32, TimedEmoteList*>::iterator i = m_timedemotes.begin(); i != m_timedemotes.end(); ++i)
{
for (TimedEmoteList::iterator i2 = i->second->begin(); i2 != i->second->end(); ++i2)
if ((*i2))
{
delete[](*i2)->msg;
delete(*i2);
}
delete i->second;
}
sLogger.info("ObjectMgr : Clearing Charters...");
for (auto& charter : m_charters)
charter.clear();
sLogger.info("ObjectMgr : Deleting Reputation Tables...");
for (ReputationModMap::iterator itr = m_reputation_creature.begin(); itr != m_reputation_creature.end(); ++itr)
{
ReputationModifier* mod = itr->second;
mod->mods.clear();
delete mod;
}
for (ReputationModMap::iterator itr = m_reputation_faction.begin(); itr != m_reputation_faction.end(); ++itr)
{
ReputationModifier* mod = itr->second;
mod->mods.clear();
delete mod;
}
for (std::unordered_map<uint32, InstanceReputationModifier*>::iterator itr = this->m_reputation_instance.begin(); itr != this->m_reputation_instance.end(); ++itr)
{
InstanceReputationModifier* mod = itr->second;
mod->mods.clear();
delete mod;
}
sLogger.info("ObjectMgr : Deleting Groups...");
for (GroupMap::iterator itr = m_groups.begin(); itr != m_groups.end();)
{
Group* pGroup = itr->second;
++itr;
if (pGroup != nullptr)
{
for (uint32 i = 0; i < pGroup->GetSubGroupCount(); ++i)
{
SubGroup* pSubGroup = pGroup->GetSubGroup(i);
if (pSubGroup != nullptr)
{
pSubGroup->Disband();
}
}
delete pGroup;
}
}
sLogger.info("ObjectMgr : Clearing Player Information...");
for (auto& itr : m_cachedCharacterInfo)
itr.second->m_Group = nullptr;
m_cachedCharacterInfo.clear();
sLogger.info("ObjectMgr : Clearing Boss Information...");
m_dungeonEncounterStore.clear();
sLogger.info("ObjectMgr : Clearing Arena Teams...");
m_arenaTeams.clear();
#ifdef FT_VEHICLES
sLogger.info("ObjectMgr : Cleaning up vehicle accessories...");
_vehicleAccessoryStore.clear();
_vehicleSeatAddonStore.clear();
#endif
sLogger.info("ObjectMgr : Cleaning up worldstate templates...");
for (std::map< uint32, std::multimap< uint32, WorldState >* >::iterator itr = m_worldstateTemplates.begin(); itr != m_worldstateTemplates.end(); ++itr)
{
itr->second->clear();
delete itr->second;
}
m_worldstateTemplates.clear();
m_creatureDisplayInfoData.clear();
sLogger.info("ObjectMgr : Clearing up event scripts...");
mEventScriptMaps.clear();
mSpellEffectMaps.clear();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Arena Team
void ObjectMgr::loadArenaTeams()
{
QueryResult* result = CharacterDatabase.Query("SELECT * FROM arenateams");
if (result != nullptr)
{
if (result->GetFieldCount() != 22)
{
sLogger.failure("arenateams table format is invalid. Please update your database.");
return;
}
do
{
const std::shared_ptr<ArenaTeam> team = std::make_shared<ArenaTeam>(result->Fetch());
addArenaTeam(team);
if (team->m_id > static_cast<uint32_t>(m_hiArenaTeamId.load()))
m_hiArenaTeamId = static_cast<uint32_t>(team->m_id);
} while (result->NextRow());
delete result;
}
updateArenaTeamRankings();
}
void ObjectMgr::addArenaTeam(std::shared_ptr<ArenaTeam> _arenaTeam)
{
std::lock_guard guard(m_arenaTeamLock);
m_arenaTeams[_arenaTeam->m_id] = _arenaTeam;
m_arenaTeamMap[_arenaTeam->m_type].insert(std::make_pair(_arenaTeam->m_id, _arenaTeam));
}
void ObjectMgr::removeArenaTeam(std::shared_ptr<ArenaTeam> _arenaTeam)
{
std::lock_guard guard(m_arenaTeamLock);
m_arenaTeams.erase(_arenaTeam->m_id);
m_arenaTeamMap[_arenaTeam->m_type].erase(_arenaTeam->m_id);
}
std::shared_ptr<ArenaTeam> ObjectMgr::getArenaTeamByName(std::string& _name, uint32_t /*type*/)
{
std::lock_guard guard(m_arenaTeamLock);
for (auto& arenaTeam : m_arenaTeams)
if (arenaTeam.second->m_name == _name)
return arenaTeam.second;
return nullptr;
}
std::shared_ptr<ArenaTeam> ObjectMgr::getArenaTeamById(uint32_t _id)
{
std::lock_guard guard(m_arenaTeamLock);
const auto arenaTeam = m_arenaTeams.find(_id);
return arenaTeam == m_arenaTeams.end() ? nullptr : arenaTeam->second;
}
std::shared_ptr<ArenaTeam> ObjectMgr::getArenaTeamByGuid(uint32_t _guid, uint32_t _type)
{
std::lock_guard guard(m_arenaTeamLock);
for (auto& arenaTeam : m_arenaTeamMap[_type])
{
if (arenaTeam.second->isMember(_guid))
return arenaTeam.second;
}
return nullptr;
}
class ArenaSorter
{
public:
bool operator()(std::shared_ptr<ArenaTeam> const& _arenaTeamA, std::shared_ptr<ArenaTeam> const& _arenaTeamB) const
{
return (_arenaTeamA->m_stats.rating > _arenaTeamB->m_stats.rating);
}
bool operator()(std::shared_ptr<ArenaTeam>& _arenaTeamA, std::shared_ptr<ArenaTeam>& _arenaTeamB) const
{
return (_arenaTeamA->m_stats.rating > _arenaTeamB->m_stats.rating);
}
};
void ObjectMgr::updateArenaTeamRankings()
{
std::lock_guard guard(m_arenaTeamLock);
for (auto& arenaTeams : m_arenaTeamMap)
{
std::vector<std::shared_ptr<ArenaTeam>> ranking;
ranking.reserve(arenaTeams.size());
for (auto& arenaTeamPair : arenaTeams)
ranking.push_back(arenaTeamPair.second);
std::ranges::sort(ranking, ArenaSorter());
uint32_t rank = 1;
for (const auto& arenaTeam : ranking)
{
if (arenaTeam->m_stats.ranking != rank)
{
arenaTeam->m_stats.ranking = rank;
arenaTeam->saveToDB();
}
++rank;
}
}
}
void ObjectMgr::updateArenaTeamWeekly()
{
std::lock_guard guard(m_arenaTeamLock);
for (auto& arenaTeams : m_arenaTeamMap)
{
for (const auto& arenaTeamPair : arenaTeams)
{
if (const std::shared_ptr<ArenaTeam> arenaTeam = arenaTeamPair.second)
{
arenaTeam->m_stats.played_week = 0;
arenaTeam->m_stats.won_week = 0;
for (uint32_t j = 0; j < arenaTeam->m_memberCount; ++j)
{
arenaTeam->m_members[j].Played_ThisWeek = 0;
arenaTeam->m_members[j].Won_ThisWeek = 0;
}
arenaTeam->saveToDB();
}
}
}
}
void ObjectMgr::resetArenaTeamRatings()
{
std::lock_guard guard(m_arenaTeamLock);
for (auto& arenaTeams : m_arenaTeamMap)
{
for (auto& arenaTeamPair : arenaTeams)
{
if (const std::shared_ptr<ArenaTeam> arenaTeam = arenaTeamPair.second)
{
arenaTeam->m_stats.played_season = 0;
arenaTeam->m_stats.played_week = 0;
arenaTeam->m_stats.won_season = 0;
arenaTeam->m_stats.won_week = 0;
arenaTeam->m_stats.rating = 1500;
for (uint32_t j = 0; j < arenaTeam->m_memberCount; ++j)
{
arenaTeam->m_members[j].Played_ThisSeason = 0;
arenaTeam->m_members[j].Played_ThisWeek = 0;
arenaTeam->m_members[j].Won_ThisSeason = 0;
arenaTeam->m_members[j].Won_ThisWeek = 0;
arenaTeam->m_members[j].PersonalRating = 1500;
}
arenaTeam->saveToDB();
}
}
}
updateArenaTeamRankings();
}
//////////////////////////////////////////////////////////////////////////////////////////
// Charter
void ObjectMgr::loadCharters()
{
m_hiCharterId = 0;
if (QueryResult* result = CharacterDatabase.Query("SELECT * FROM charters"))
{
do
{
auto charter = std::make_shared<Charter>(result->Fetch());
m_charters[charter->getCharterType()].insert(std::make_pair(charter->getId(), charter));
if (charter->getId() > static_cast<int64_t>(m_hiCharterId.load()))
m_hiCharterId = charter->getId();
} while (result->NextRow());
delete result;
}
sLogger.info("ObjectMgr : %u charters loaded.", static_cast<uint32_t>(m_charters[0].size()));
}
void ObjectMgr::removeCharter(const std::shared_ptr<Charter>& _charter)
{
if (_charter)
{
if (_charter->getCharterType() >= NUM_CHARTER_TYPES)
{
sLogger.debug("ObjectMgr : Charter %u cannot be destroyed as type %u is not a valid type.", _charter->getId(), static_cast<uint32_t>(_charter->getCharterType()));
return;
}
std::lock_guard guard(m_charterLock);
m_charters[_charter->getCharterType()].erase(_charter->getId());
}
}
std::shared_ptr<Charter> ObjectMgr::createCharter(uint32_t _leaderGuid, CharterTypes _type)
{
uint32_t charterId = ++m_hiCharterId;
auto charter = std::make_shared<Charter>(charterId, _leaderGuid, _type);
std::lock_guard guard(m_charterLock);
m_charters[charter->getCharterType()].insert(std::make_pair(charter->getId(), charter));
return charter;
}
std::shared_ptr<Charter> ObjectMgr::getCharterByName(const std::string& _charterName, const CharterTypes _type)
{
std::lock_guard guard(m_charterLock);
for (auto& charterPair : m_charters[_type])
if (charterPair.second->getGuildName() == _charterName)
return charterPair.second;
return nullptr;
}
std::shared_ptr<Charter> ObjectMgr::getCharter(const uint32_t _charterId, const CharterTypes _type)
{
std::lock_guard guard(m_charterLock);
const auto charterPair = m_charters[_type].find(_charterId);
return charterPair == m_charters[_type].end() ? nullptr : charterPair->second;
}
std::shared_ptr<Charter> ObjectMgr::getCharterByGuid(const uint64_t _playerGuid, const CharterTypes _type)
{
std::lock_guard guard(m_charterLock);
for (auto& charterPair : m_charters[_type])
{
if (_playerGuid == charterPair.second->getLeaderGuid())
return charterPair.second;
for (const uint32_t playerGuid : charterPair.second->getSignatures())
if (playerGuid == _playerGuid)
return charterPair.second;
}
return nullptr;
}
std::shared_ptr<Charter> ObjectMgr::getCharterByItemGuid(const uint64_t _itemGuid)
{
std::lock_guard guard(m_charterLock);
for (auto& charterType : m_charters)
{
for (auto& charterPair : charterType)
if (charterPair.second->getItemGuid() == _itemGuid)
return charterPair.second;
}
return nullptr;
}
//////////////////////////////////////////////////////////////////////////////////////////
// CachedCharacterInfo
void ObjectMgr::loadCharacters()
{
QueryResult* result = CharacterDatabase.Query("SELECT guid, name, race, class, level, gender, zoneid, timestamp, acct FROM characters");
if (result)
{
do
{
Field* fields = result->Fetch();
const auto cachedCharacterInfo = std::make_shared<CachedCharacterInfo>();
cachedCharacterInfo->guid = fields[0].GetUInt32();
std::string characterNameDB = fields[1].GetString();
AscEmu::Util::Strings::capitalize(characterNameDB);
cachedCharacterInfo->name = characterNameDB;
cachedCharacterInfo->race = fields[2].GetUInt8();
cachedCharacterInfo->cl = fields[3].GetUInt8();
cachedCharacterInfo->lastLevel = fields[4].GetUInt32();
cachedCharacterInfo->gender = fields[5].GetUInt8();
cachedCharacterInfo->lastZone = fields[6].GetUInt32();
cachedCharacterInfo->lastOnline = fields[7].GetUInt32();
cachedCharacterInfo->acct = fields[8].GetUInt32();
cachedCharacterInfo->m_Group = nullptr;
cachedCharacterInfo->subGroup = 0;
cachedCharacterInfo->m_guild = 0;
cachedCharacterInfo->guildRank = GUILD_RANK_NONE;
cachedCharacterInfo->team = getSideByRace(cachedCharacterInfo->race);
m_cachedCharacterInfo[cachedCharacterInfo->guid] = cachedCharacterInfo;
} while (result->NextRow());
delete result;
}
sLogger.info("ObjectMgr : %u players loaded.", static_cast<uint32_t>(m_cachedCharacterInfo.size()));
}
void ObjectMgr::addCachedCharacterInfo(const std::shared_ptr<CachedCharacterInfo>& _characterInfo)
{
std::lock_guard guard(m_cachedCharacterLock);
m_cachedCharacterInfo[_characterInfo->guid] = _characterInfo;
}
std::shared_ptr<CachedCharacterInfo> ObjectMgr::getCachedCharacterInfo(uint32_t _playerGuid)
{
std::lock_guard guard(m_cachedCharacterLock);
const auto characterPair = m_cachedCharacterInfo.find(_playerGuid);
if (characterPair != m_cachedCharacterInfo.end())
return characterPair->second;
return nullptr;
}
std::shared_ptr<CachedCharacterInfo> ObjectMgr::getCachedCharacterInfoByName(std::string _playerName)
{
std::string searchName = std::string(std::move(_playerName));
AscEmu::Util::Strings::toLowerCase(searchName);
std::lock_guard guard(m_cachedCharacterLock);
for (const auto characterPair : m_cachedCharacterInfo)
{
std::string characterName = characterPair.second->name;
AscEmu::Util::Strings::toLowerCase(characterName);
if (characterName == searchName)
return characterPair.second;
}
return nullptr;
}
void ObjectMgr::updateCachedCharacterInfoName(const std::shared_ptr<CachedCharacterInfo>& _characterInfo, const std::string& _newName)
{
std::lock_guard guard(m_cachedCharacterLock);
for (const auto& characterPair : m_cachedCharacterInfo)
if (_characterInfo == characterPair.second)
characterPair.second->name = _newName;
}
void ObjectMgr::deleteCachedCharacterInfo(const uint32_t _playerGuid)
{
std::lock_guard guard(m_cachedCharacterLock);
const auto characterPair = m_cachedCharacterInfo.find(_playerGuid);
if (characterPair == m_cachedCharacterInfo.end())
return;
const auto characterInfo = characterPair->second;
if (characterInfo->m_Group)
characterInfo->m_Group->RemovePlayer(characterInfo);
m_cachedCharacterInfo.erase(characterPair);
}
//////////////////////////////////////////////////////////////////////////////////////////
// Corpse
void ObjectMgr::loadCorpsesForInstance(WorldMap* _worldMap) const
{
if (QueryResult* result = CharacterDatabase.Query("SELECT * FROM corpses WHERE instanceid = %u", _worldMap->getInstanceId()))
{
do
{
Field* fields = result->Fetch();
const auto corpse = std::make_shared<Corpse>(HIGHGUID_TYPE_CORPSE, fields[0].GetUInt32());
corpse->SetPosition(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
corpse->setZoneId(fields[5].GetUInt32());
corpse->SetMapId(fields[6].GetUInt32());
corpse->SetInstanceID(fields[7].GetUInt32());
corpse->setCorpseDataFromDbString(fields[8].GetString());
if (corpse->getDisplayId() == 0)
continue;
corpse->PushToWorld(_worldMap);
} while (result->NextRow());
delete result;
}
}
std::shared_ptr<Corpse> ObjectMgr::loadCorpseByGuid(const uint32_t _corpseGuid) const
{
if (QueryResult* result = CharacterDatabase.Query("SELECT * FROM corpses WHERE guid =%u ", _corpseGuid))
{
Field* field = result->Fetch();
const auto corpse = std::make_shared<Corpse>(HIGHGUID_TYPE_CORPSE, field[0].GetUInt32());
corpse->SetPosition(field[1].GetFloat(), field[2].GetFloat(), field[3].GetFloat(), field[4].GetFloat());
corpse->setZoneId(field[5].GetUInt32());
corpse->SetMapId(field[6].GetUInt32());
corpse->setCorpseDataFromDbString(field[7].GetString());
if (corpse->getDisplayId() == 0)
return nullptr;
corpse->setLoadedFromDB(true);
corpse->SetInstanceID(field[8].GetUInt32());
corpse->AddToWorld();
delete result;
return corpse;
}
return nullptr;
}
std::shared_ptr<Corpse> ObjectMgr::createCorpse()
{
uint32_t corpseGuid = ++m_hiCorpseGuid;
return std::make_shared<Corpse>(HIGHGUID_TYPE_CORPSE, corpseGuid);
}
void ObjectMgr::addCorpse(const std::shared_ptr<Corpse>& _corpse)
{
std::lock_guard guard(m_corpseLock);
m_corpses[_corpse->getGuidLow()] = _corpse;
}
void ObjectMgr::removeCorpse(const std::shared_ptr<Corpse>& _corpse)
{
std::lock_guard guard(m_corpseLock);
m_corpses.erase(_corpse->getGuidLow());
}
std::shared_ptr<Corpse> ObjectMgr::getCorpseByGuid(uint32_t _corpseGuid)
{
std::lock_guard guard(m_corpseLock);
const auto corpsePair = m_corpses.find(_corpseGuid);
return corpsePair != m_corpses.end() ? corpsePair->second : nullptr;
}
std::shared_ptr<Corpse> ObjectMgr::getCorpseByOwner(const uint32_t _playerGuid)
{
std::lock_guard guard(m_corpseLock);
for (const auto& corpsePair : m_corpses)
{
WoWGuid wowGuid;
wowGuid.Init(corpsePair.second->getOwnerGuid());
if (wowGuid.getGuidLowPart() == _playerGuid)
return corpsePair.second;
}
return nullptr;
}
void ObjectMgr::unloadCorpseCollector()
{
std::lock_guard guard(m_corpseLock);
for (const auto& corpsePair : m_corpses)
{
const auto corpse = corpsePair.second;
if (corpse->IsInWorld())
corpse->RemoveFromWorld(false);
}
m_corpses.clear();
}
void ObjectMgr::addCorpseDespawnTime(const std::shared_ptr<Corpse>& _corpse)
{
if (_corpse->IsInWorld())
_corpse->getWorldMap()->addCorpseDespawn(_corpse->getGuid(), 600000);
}
void ObjectMgr::delinkCorpseForPlayer(const Player* _player)
{
if (const auto corpse = getCorpseByOwner(_player->getGuidLow()))
{
corpse->delink();
addCorpseDespawnTime(corpse);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// Achievement
#if VERSION_STRING > TBC
void ObjectMgr::loadAchievementCriteriaList()
{
for (uint32 rowId = 0; rowId < sAchievementCriteriaStore.GetNumRows(); ++rowId)
{
auto criteria = sAchievementCriteriaStore.LookupEntry(rowId);
if (!criteria)
continue;
#if VERSION_STRING > WotLK
auto achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
if (achievement && achievement->flags & ACHIEVEMENT_FLAG_GUILD)
m_GuildAchievementCriteriasByType[criteria->requiredType].push_back(criteria);
else
#endif
m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
}
}
void ObjectMgr::loadAchievementRewards()
{
m_achievementRewards.clear();
QueryResult* result = WorldDatabase.Query("SELECT entry, gender, title_A, title_H, item, sender, subject, text FROM achievement_reward");
if (!result)
{
sLogger.info("Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
if (!sAchievementStore.LookupEntry(entry))
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : Achievement reward entry %u has wrong achievement, ignore", entry);
continue;
}
AchievementReward reward;
reward.gender = fields[1].GetUInt8();
reward.titel_A = fields[2].GetUInt32();
reward.titel_H = fields[3].GetUInt32();
reward.itemId = fields[4].GetUInt32();
reward.sender = fields[5].GetUInt32();
reward.subject = fields[6].GetString() ? fields[6].GetString() : "";
reward.text = fields[7].GetString() ? fields[7].GetString() : "";
if (reward.gender > 2)
sLogger.debug("ObjectMgr : achievement reward %u has wrong gender %u.", entry, static_cast<uint32_t>(reward.gender));
bool dup = false;
AchievementRewardsMapBounds bounds = m_achievementRewards.equal_range(entry);
for (AchievementRewardsMap::const_iterator iter = bounds.first; iter != bounds.second; ++iter)
{
if (iter->second.gender == 2 || reward.gender == 2)
{
dup = true;
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : Achievement reward %u must have single GENDER_NONE (%u), ignore duplicate case", 2, entry);
break;
}
}
if (dup)
continue;
// must be title or mail at least
if (!reward.titel_A && !reward.titel_H && !reward.sender)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u not have title or item reward data, ignore.", entry);
continue;
}
if (reward.titel_A)
{
auto const* char_title_entry = sCharTitlesStore.LookupEntry(reward.titel_A);
if (!char_title_entry)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u has invalid title id (%u) in `title_A`, set to 0", entry, reward.titel_A);
reward.titel_A = 0;
}
}
if (reward.titel_H)
{
auto const* char_title_entry = sCharTitlesStore.LookupEntry(reward.titel_H);
if (!char_title_entry)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u has invalid title id (%u) in `title_A`, set to 0", entry, reward.titel_H);
reward.titel_H = 0;
}
}
//check mail data before item for report including wrong item case
if (reward.sender)
{
if (!sMySQLStore.getCreatureProperties(reward.sender))
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender);
reward.sender = 0;
}
}
else
{
if (reward.itemId)
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u not have sender data but have item reward, item will not rewarded", entry);
if (!reward.subject.empty())
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u not have sender data but have mail subject.", entry);
if (!reward.text.empty())
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u not have sender data but have mail text.", entry);
}
if (reward.itemId == 0)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "ObjectMgr : achievement_reward %u has invalid item id %u, reward mail will be without item.", entry, reward.itemId);
}
m_achievementRewards.insert(AchievementRewardsMap::value_type(entry, reward));
++count;
} while (result->NextRow());
delete result;
sLogger.info("ObjectMgr : Loaded %u achievement rewards", count);
}
void ObjectMgr::loadCompletedAchievements()
{
QueryResult* result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
if (!result)
{
sLogger.failure("Query failed: SELECT achievement FROM character_achievement");
return;
}
do
{
Field* fields = result->Fetch();
m_allCompletedAchievements.insert(fields[0].GetUInt32());
} while (result->NextRow());
delete result;
}
AchievementReward const* ObjectMgr::getAchievementReward(uint32_t _entry, uint8_t _gender)
{
AchievementRewardsMapBounds bounds = m_achievementRewards.equal_range(_entry);
for (AchievementRewardsMap::const_iterator iter = bounds.first; iter != bounds.second; ++iter)
{
if (iter->second.gender == 2 || iter->second.gender == _gender)
return &iter->second;
}
return nullptr;
}
AchievementCriteriaEntryList const& ObjectMgr::getAchievementCriteriaByType(AchievementCriteriaTypes _type)
{
return m_AchievementCriteriasByType[_type];
}
void ObjectMgr::addCompletedAchievement(uint32_t _achievementId)
{
m_allCompletedAchievements.insert(_achievementId);
}
std::set<uint32_t> ObjectMgr::getAllCompleteAchievements()
{
return m_allCompletedAchievements;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
// Misc
void ObjectMgr::generateDatabaseGossipMenu(Object* _object, uint32_t _gossipMenuId, Player* _player, uint32_t _forcedTextId /*= 0*/)
{
uint32_t textId = 2;
if (_forcedTextId == 0)
{
auto gossipMenuTextStore = sMySQLStore.getGossipMenuInitTextId();
for (auto& initItr : *gossipMenuTextStore)
{
if (initItr.first == _gossipMenuId)
{
textId = initItr.second.textId;
break;
}
}
}
else
{
textId = _forcedTextId;
}
GossipMenu menu(_object->getGuid(), textId, _player->getSession()->language, _gossipMenuId);
sQuestMgr.FillQuestMenu(dynamic_cast<Creature*>(_object), _player, menu);
typedef MySQLDataStore::GossipMenuItemsContainer::iterator GossipMenuItemsIterator;
std::pair<GossipMenuItemsIterator, GossipMenuItemsIterator> gossipEqualRange = sMySQLStore._gossipMenuItemsStores.equal_range(_gossipMenuId);
for (GossipMenuItemsIterator itr = gossipEqualRange.first; itr != gossipEqualRange.second; ++itr)
{
// check requirements
// 0 = none
// 1 = has(active)Quest
// 2 = has(finished)Quest
// 3 = canGainXP
// 4 = canNotGainXP
if (itr->first == _gossipMenuId)
{
auto& gossipMenuItem = itr->second;
if (gossipMenuItem.requirementType == 1 && !_player->hasQuestInQuestLog(gossipMenuItem.requirementData))
continue;
if (gossipMenuItem.requirementType == 3)
{
if (_player->canGainXp())
menu.addItem(gossipMenuItem.icon, gossipMenuItem.menuOptionText, gossipMenuItem.itemOrder, "", gossipMenuItem.onChooseData, _player->getSession()->LocalizedGossipOption(gossipMenuItem.onChooseData2));
continue;
}
if (gossipMenuItem.requirementType == 4)
{
if (!_player->canGainXp())
menu.addItem(gossipMenuItem.icon, gossipMenuItem.menuOptionText, gossipMenuItem.itemOrder, "", gossipMenuItem.onChooseData, _player->getSession()->LocalizedGossipOption(gossipMenuItem.onChooseData2));
continue;
}
menu.addItem(gossipMenuItem.icon, gossipMenuItem.menuOptionText, gossipMenuItem.itemOrder);
}
}
menu.sendGossipPacket(_player);
}
void ObjectMgr::generateDatabaseGossipOptionAndSubMenu(Object* _object, Player* _player, uint32_t _gossipItemId, uint32_t _gossipMenuId)
{
sLogger.debug("GossipId: %u gossipItemId: %u", _gossipMenuId, _gossipItemId);
// bool openSubMenu = true;
typedef MySQLDataStore::GossipMenuItemsContainer::iterator GossipMenuItemsIterator;
std::pair<GossipMenuItemsIterator, GossipMenuItemsIterator> gossipEqualRange = sMySQLStore._gossipMenuItemsStores.equal_range(_gossipMenuId);
for (GossipMenuItemsIterator itr = gossipEqualRange.first; itr != gossipEqualRange.second; ++itr)
{
if (itr->second.itemOrder == _gossipItemId)
{
// onChooseAction
// 0 = None
// 1 = sendPoiById (on_choose_data = poiId)
// 2 = castSpell (on_choose_data = spellId)
// 3 = sendTaxi (on_choose_data = taxiId, on_choose_data2 = modelId)
// 4 = required standing (on_choose_data = factionId, on_choose_data2 = standing, on_choose_data3 = broadcastTextId)
// 5 = close window
// 6 = toggleXPGain
// onChooseData
// depending on Action...
switch (itr->second.onChooseAction)
{
case 1:
{
generateDatabaseGossipMenu(_object, itr->second.nextGossipMenu, _player, itr->second.nextGossipMenuText);
if (itr->second.onChooseData != 0)
_player->sendPoiById(itr->second.onChooseData);
} break;
case 2:
{
if (itr->second.onChooseData != 0)
{
_player->castSpell(_player, sSpellMgr.getSpellInfo(itr->second.onChooseData), true);
GossipMenu::senGossipComplete(_player);
}
} break;
case 3:
{
if (itr->second.onChooseData != 0)
{
if (_object->isCreature())
_player->getSession()->sendTaxiMenu(_object->ToCreature());
GossipMenu::senGossipComplete(_player);
}
} break;
case 4:
{
if (itr->second.onChooseData != 0)
{
if (_player->getFactionStanding(itr->second.onChooseData) >= static_cast<int32_t>(itr->second.onChooseData2))
_player->castSpell(_player, sSpellMgr.getSpellInfo(itr->second.onChooseData3), true);
else
_player->broadcastMessage(_player->getSession()->LocalizedWorldSrv(itr->second.onChooseData4));
GossipMenu::senGossipComplete(_player);
}
} break;
case 5:
{
GossipMenu::senGossipComplete(_player);
} break;
case 6:
{
if (_player->hasEnoughCoinage(itr->second.onChooseData))
{
_player->modCoinage(-static_cast<int32_t>(itr->second.onChooseData));
_player->toggleXpGain();
GossipMenu::senGossipComplete(_player);
}
} break;
default: // action 0
{
generateDatabaseGossipMenu(_object, itr->second.nextGossipMenu, _player, itr->second.nextGossipMenuText);
} break;
}
}
}
}
void ObjectMgr::loadTrainerSpellSets()
{
auto* const spellSetResult = sMySQLStore.getWorldDBQuery("SELECT * FROM trainer_properties_spellset WHERE min_build <= %u AND max_build >= %u;", VERSION_STRING, VERSION_STRING);
if (spellSetResult != nullptr)
{
std::unordered_map<uint32_t, std::vector<TrainerSpell>*>::const_iterator itr;
std::vector<TrainerSpell>* trainerSpells;
do
{
Field* fields = spellSetResult->Fetch();
itr = m_trainerSpellSet.find(fields[0].GetUInt32());
if (itr == m_trainerSpellSet.end())
{
trainerSpells = new std::vector<TrainerSpell>;
m_trainerSpellSet[fields[0].GetUInt32()] = trainerSpells;
}
else
{
trainerSpells = itr->second;
}
auto* const fields2 = spellSetResult->Fetch();
auto castSpellID = fields2[3].GetUInt32();
auto learnSpellID = fields2[4].GetUInt32();
TrainerSpell ts;
auto abrt = false;
if (castSpellID != 0)
{
ts.castSpell = sSpellMgr.getSpellInfo(castSpellID);
if (ts.castSpell != nullptr)
{
// Check that the castable spell has learn spell effect
for (uint8_t i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (ts.castSpell->getEffect(i) == SPELL_EFFECT_LEARN_SPELL)
{
ts.castRealSpell = sSpellMgr.getSpellInfo(ts.castSpell->getEffectTriggerSpell(i));
if (ts.castRealSpell == nullptr)
{
sLogger.failure("LoadTrainers : TrainerSpellSet %u contains cast spell %u that is non-teaching", fields[0].GetUInt32(), castSpellID);
abrt = true;
}
break;
}
}
}
if (abrt)
continue;
}
if (learnSpellID != 0)
ts.learnSpell = sSpellMgr.getSpellInfo(learnSpellID);
if (ts.castSpell == nullptr && ts.learnSpell == nullptr)
continue;
if (ts.castSpell != nullptr && ts.castRealSpell == nullptr)
continue;
ts.cost = fields2[5].GetUInt32();
ts.requiredSpell[0] = fields2[6].GetUInt32();
ts.requiredSkillLine = fields2[7].GetUInt16();
ts.requiredSkillLineValue = fields2[8].GetUInt32();
ts.requiredLevel = fields2[9].GetUInt32();
ts.deleteSpell = fields2[10].GetUInt32();
ts.isStatic = fields2[11].GetUInt32();
// Check if spell teaches a primary profession skill
if (ts.requiredSkillLine == 0 && ts.castRealSpell != nullptr)
ts.isPrimaryProfession = ts.castRealSpell->isPrimaryProfession();
// Add all required spells
const auto spellInfo = ts.castRealSpell != nullptr ? ts.castSpell : ts.learnSpell;
const auto requiredSpells = sSpellMgr.getSpellsRequiredForSpellBounds(spellInfo->getId());
for (auto itr = requiredSpells.first; itr != requiredSpells.second; ++itr)
{
for (uint8_t i = 0; i < 3; ++i)
{
if (ts.requiredSpell[i] == itr->second)
break;
if (ts.requiredSpell[i] != 0)
continue;
ts.requiredSpell[i] = itr->second;
break;
}
}
trainerSpells->push_back(ts);
} while (spellSetResult->NextRow());
sLogger.info("LoadTrainers : %u TrainerSpellSet loaded", static_cast<uint32_t>(m_trainerSpellSet.size()));
}
}
std::vector<TrainerSpell> ObjectMgr::getTrainerSpellSetById(uint32_t _id)
{
auto itr = m_trainerSpellSet.find(_id);
if (itr == m_trainerSpellSet.end())
return {};
return *itr->second;
}
void ObjectMgr::loadTrainers()
{
if (auto* const trainerResult = sMySQLStore.getWorldDBQuery("SELECT * FROM trainer_properties WHERE build <= %u;", VERSION_STRING))
{
do
{
auto* const fields = trainerResult->Fetch();
const auto entry = fields[0].GetUInt32();
std::shared_ptr<Trainer> trainer = std::make_shared<Trainer>();
trainer->RequiredSkill = fields[2].GetUInt16();
trainer->RequiredSkillLine = fields[3].GetUInt32();
trainer->RequiredClass = fields[4].GetUInt32();
trainer->RequiredRace = fields[5].GetUInt32();
trainer->RequiredRepFaction = fields[6].GetUInt32();
trainer->RequiredRepValue = fields[7].GetUInt32();
trainer->TrainerType = fields[8].GetUInt32();
trainer->Can_Train_Gossip_TextId = fields[10].GetUInt32();
trainer->Cannot_Train_GossipTextId = fields[11].GetUInt32();
trainer->spellset_id = fields[12].GetUInt32();
trainer->can_train_max_level = fields[13].GetUInt32();
trainer->can_train_min_skill_value = fields[14].GetUInt32();
trainer->can_train_max_skill_value = fields[15].GetUInt32();
if (!trainer->Can_Train_Gossip_TextId)
trainer->Can_Train_Gossip_TextId = 1;
if (!trainer->Cannot_Train_GossipTextId)
trainer->Cannot_Train_GossipTextId = 1;
const char* temp = fields[9].GetString();
size_t len = strlen(temp);
if (len)
{
trainer->UIMessage = new char[len + 1];
strcpy(trainer->UIMessage, temp);
trainer->UIMessage[len] = 0;
}
else
{
trainer->UIMessage = new char[strlen(NormalTalkMessage) + 1];
strcpy(trainer->UIMessage, NormalTalkMessage);
trainer->UIMessage[strlen(NormalTalkMessage)] = 0;
}
trainer->SpellCount = static_cast<uint32_t>(getTrainerSpellSetById(trainer->spellset_id).size());
// and now we insert it to our lookup table
if (trainer->SpellCount == 0)
{
if (trainer->UIMessage != NormalTalkMessage)
delete[] trainer->UIMessage;
continue;
}
m_trainers.insert(std::pair(entry, trainer));
} while (trainerResult->NextRow());
delete trainerResult;
sLogger.info("ObjectMgr : %u trainers loaded.", static_cast<uint32_t>(m_trainers.size()));
}
}
std::shared_ptr<Trainer> ObjectMgr::getTrainer(uint32_t _entry)
{
const auto iter = m_trainers.find(_entry);
if (iter == m_trainers.end())
return nullptr;
return iter->second;
}
void ObjectMgr::loadCreatureDisplayInfo()
{
for (uint32_t i = 0; i < sCreatureDisplayInfoStore.GetNumRows(); ++i)
{
const auto* const displayInfoEntry = sCreatureDisplayInfoStore.LookupEntry(i);
if (displayInfoEntry == nullptr)
continue;
CreatureDisplayInfoData data;
data.id = displayInfoEntry->ID;
data.modelId = displayInfoEntry->ModelID;
data.extendedDisplayInfoId = displayInfoEntry->ExtendedDisplayInfoID;
data.creatureModelScale = displayInfoEntry->CreatureModelScale;
data.modelInfo = sCreatureModelDataStore.LookupEntry(data.modelId);
if (data.modelInfo != nullptr)
{
if (strstr(data.modelInfo->ModelName, "InvisibleStalker"))
data.isModelInvisibleStalker = true;
}
m_creatureDisplayInfoData.insert(std::make_pair(displayInfoEntry->ID, data));
}
}
CreatureDisplayInfoData const* ObjectMgr::getCreatureDisplayInfoData(uint32_t displayId) const
{
const auto itr = m_creatureDisplayInfoData.find(displayId);
if (itr == m_creatureDisplayInfoData.cend())
return nullptr;
return &itr->second;
}
Player* ObjectMgr::createPlayerByGuid(uint8_t _class, uint32_t guid)
{
Player* player;
switch (_class)
{
case WARRIOR:
player = new Warrior(guid);
break;
case PALADIN:
player = new Paladin(guid);
break;
case HUNTER:
player = new Hunter(guid);
break;
case ROGUE:
player = new Rogue(guid);
break;
case PRIEST:
player = new Priest(guid);
break;
#if VERSION_STRING > TBC
case DEATHKNIGHT:
player = new DeathKnight(guid);
break;
#endif
case SHAMAN:
player = new Shaman(guid);
break;
case MAGE:
player = new Mage(guid);
break;
case WARLOCK:
player = new Warlock(guid);
break;
#if VERSION_STRING > Cata
case MONK:
player = new Monk(guid);
break;
#endif
case DRUID:
player = new Druid(guid);
break;
default:
player = nullptr;
break;
}
return player;
}
GameObject* ObjectMgr::createGameObjectByGuid(uint32_t id, uint32_t guid)
{
GameObjectProperties const* gameobjectProperties = sMySQLStore.getGameObjectProperties(id);
if (gameobjectProperties == nullptr)
return nullptr;
GameObject* gameObject;
const uint64_t createdGuid = uint64_t((uint64_t(HIGHGUID_TYPE_GAMEOBJECT) << 32) | guid);
switch (gameobjectProperties->type)
{
case GAMEOBJECT_TYPE_DOOR:
gameObject = new GameObject_Door(createdGuid);
break;
case GAMEOBJECT_TYPE_BUTTON:
gameObject = new GameObject_Button(createdGuid);
break;
case GAMEOBJECT_TYPE_QUESTGIVER:
gameObject = new GameObject_QuestGiver(createdGuid);
break;
case GAMEOBJECT_TYPE_CHEST:
gameObject = new GameObject_Chest(createdGuid);
break;
case GAMEOBJECT_TYPE_TRAP:
gameObject = new GameObject_Trap(createdGuid);
break;
case GAMEOBJECT_TYPE_CHAIR:
gameObject = new GameObject_Chair(createdGuid);
break;
case GAMEOBJECT_TYPE_SPELL_FOCUS:
gameObject = new GameObject_SpellFocus(createdGuid);
break;
case GAMEOBJECT_TYPE_GOOBER:
gameObject = new GameObject_Goober(createdGuid);
break;
case GAMEOBJECT_TYPE_TRANSPORT:
gameObject = new GameObject_Transport(createdGuid);
break;
case GAMEOBJECT_TYPE_CAMERA:
gameObject = new GameObject_Camera(createdGuid);
break;
case GAMEOBJECT_TYPE_FISHINGNODE:
gameObject = new GameObject_FishingNode(createdGuid);
break;
case GAMEOBJECT_TYPE_RITUAL:
gameObject = new GameObject_Ritual(createdGuid);
break;
case GAMEOBJECT_TYPE_SPELLCASTER:
gameObject = new GameObject_SpellCaster(createdGuid);
break;
case GAMEOBJECT_TYPE_MEETINGSTONE:
gameObject = new GameObject_Meetingstone(createdGuid);
break;
case GAMEOBJECT_TYPE_FLAGSTAND:
gameObject = new GameObject_FlagStand(createdGuid);
break;
case GAMEOBJECT_TYPE_FISHINGHOLE:
gameObject = new GameObject_FishingHole(createdGuid);
break;
case GAMEOBJECT_TYPE_FLAGDROP:
gameObject = new GameObject_FlagDrop(createdGuid);
break;
case GAMEOBJECT_TYPE_BARBER_CHAIR:
gameObject = new GameObject_BarberChair(createdGuid);
break;
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
gameObject = new GameObject_Destructible(createdGuid);
break;
default:
gameObject = new GameObject(createdGuid);
break;
}
gameObject->SetGameObjectProperties(gameobjectProperties);
return gameObject;
}
DungeonEncounterList const* ObjectMgr::getDungeonEncounterList(uint32_t _mapId, uint8_t _difficulty)
{
#if VERSION_STRING >= WotLK
std::unordered_map<uint32_t, DungeonEncounterList>::const_iterator itr = m_dungeonEncounterStore.find(uint32_t(uint16_t(_mapId) | (uint32_t(_difficulty) << 16)));
#else
std::unordered_map<uint32_t, DungeonEncounterList>::const_iterator itr = _dungeonEncounterStore.find(_mapId);
#endif
if (itr != m_dungeonEncounterStore.end())
return &itr->second;
return nullptr;
}
void ObjectMgr::loadCreatureMovementOverrides()
{
const auto startTime = Util::TimeNow();
uint32_t count = 0;
m_creatureMovementOverrides.clear();
QueryResult* result = WorldDatabase.Query("SELECT SpawnId, Ground, Swim, Flight, Rooted, Chase, Random from creature_movement_override");
if (!result)
{
sLogger.info("loadCreatureMovementOverrides : Loaded 0 creature movement overrides. DB table `creature_movement_override` is empty!");
return;
}
do
{
Field* fields = result->Fetch();
uint32_t spawnId = fields[0].GetUInt32();
QueryResult* spawnResult = WorldDatabase.Query("SELECT * FROM creature_spawns WHERE id = %u", spawnId);
if (spawnResult == nullptr)
{
sLogger.failure("Creature (SpawnId: %u) does not exist but has a record in `creature_movement_override`", spawnId);
delete spawnResult;
continue;
}
CreatureMovementData& movement = m_creatureMovementOverrides[spawnId];
movement.Ground = static_cast<CreatureGroundMovementType>(fields[1].GetUInt8());
movement.Swim = fields[2].GetBool();
movement.Flight = static_cast<CreatureFlightMovementType>(fields[3].GetUInt8());
movement.Rooted = fields[4].GetBool();
movement.Chase = static_cast<CreatureChaseMovementType>(fields[5].GetUInt8());
movement.Random = static_cast<CreatureRandomMovementType>(fields[6].GetUInt8());
checkCreatureMovement(spawnId, movement);
++count;
} while (result->NextRow());
delete result;
sLogger.info("ObjectMgr : Loaded %u movement overrides in %u ms", count, static_cast<uint32_t>(Util::GetTimeDifferenceToNow(startTime)));
}
void ObjectMgr::checkCreatureMovement(uint32_t /*id*/, CreatureMovementData& _creatureMovement)
{
if (_creatureMovement.Ground >= CreatureGroundMovementType::Max)
_creatureMovement.Ground = CreatureGroundMovementType::Run;
if (_creatureMovement.Flight >= CreatureFlightMovementType::Max)
_creatureMovement.Flight = CreatureFlightMovementType::None;
if (_creatureMovement.Chase >= CreatureChaseMovementType::Max)
_creatureMovement.Chase = CreatureChaseMovementType::Run;
if (_creatureMovement.Random >= CreatureRandomMovementType::Max)
_creatureMovement.Random = CreatureRandomMovementType::Walk;
}
CreatureMovementData const* ObjectMgr::getCreatureMovementOverride(uint32_t _spawnId) const
{
const auto itr = m_creatureMovementOverrides.find(_spawnId);
if (itr != m_creatureMovementOverrides.end())
return &itr->second;
return nullptr;
}
void ObjectMgr::loadWorldStateTemplates()
{
QueryResult* result = WorldDatabase.QueryNA("SELECT DISTINCT map FROM worldstate_templates ORDER BY map;");
if (result == nullptr)
return;
do
{
Field* field = result->Fetch();
uint32_t mapId = field[0].GetUInt32();
m_worldstateTemplates.insert(std::make_pair(mapId, new std::multimap<uint32_t, WorldState>()));
} while (result->NextRow());
delete result;
result = WorldDatabase.QueryNA("SELECT map, zone, field, value FROM worldstate_templates;");
if (result == nullptr)
return;
do
{
Field* field = result->Fetch();
WorldState worldState;
uint32_t mapId = field[0].GetUInt32();
uint32_t zone = field[1].GetUInt32();
worldState.field = field[2].GetUInt32();
worldState.value = field[3].GetUInt32();
auto itr = m_worldstateTemplates.find(mapId);
if (itr == m_worldstateTemplates.end())
continue;
itr->second->insert(std::make_pair(zone, worldState));
} while (result->NextRow());
delete result;
}
std::multimap<uint32, WorldState>* ObjectMgr::getWorldStatesForMap(uint32 map) const
{
const auto itr = m_worldstateTemplates.find(map);
if (itr == m_worldstateTemplates.end())
return nullptr;
return itr->second;
}
//////////////////////////////////////////////////////////////////////////////////////////
// Groups
Group* ObjectMgr::GetGroupByLeader(Player* pPlayer)
{
std::lock_guard<std::mutex> guard(m_groupLock);
for (GroupMap::iterator itr = m_groups.begin(); itr != m_groups.end(); ++itr)
{
if (itr->second->GetLeader() == pPlayer->getPlayerInfo())
{
return itr->second;
}
}
return nullptr;
}
Group* ObjectMgr::GetGroupById(uint32 id)
{
std::lock_guard<std::mutex> guard(m_groupLock);
GroupMap::iterator itr = m_groups.find(id);
if (itr != m_groups.end())
return itr->second;
return nullptr;
}
void ObjectMgr::SetHighestGuids()
{
QueryResult* result = CharacterDatabase.Query("SELECT MAX(guid) FROM characters");
if (result)
{
m_hiPlayerGuid = result->Fetch()[0].GetUInt32();
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(guid) FROM playeritems");
if (result)
{
m_hiItemGuid = result->Fetch()[0].GetUInt32();
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(guid) FROM corpses");
if (result)
{
m_hiCorpseGuid = result->Fetch()[0].GetUInt32();
delete result;
}
result = sMySQLStore.getWorldDBQuery("SELECT MAX(id) FROM creature_spawns WHERE min_build <= %u AND max_build >= %u AND event_entry = 0", VERSION_STRING, VERSION_STRING);
if (result)
{
do
{
m_hiCreatureSpawnId = result->Fetch()[0].GetUInt32();
} while (result->NextRow());
delete result;
}
result = sMySQLStore.getWorldDBQuery("SELECT MAX(id) FROM gameobject_spawns WHERE min_build <= %u AND max_build >= %u AND event_entry = 0", VERSION_STRING, VERSION_STRING);
if (result)
{
do
{
m_hiGameObjectSpawnId = result->Fetch()[0].GetUInt32();
} while (result->NextRow());
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(group_id) FROM `groups`");
if (result)
{
m_hiGroupId = result->Fetch()[0].GetUInt32();
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(charterid) FROM charters");
if (result)
{
m_hiCharterId = result->Fetch()[0].GetUInt32();
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(guildid) FROM guilds");
if (result)
{
m_hiGuildId = result->Fetch()[0].GetUInt32();
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(UID) FROM playerbugreports");
if (result != nullptr)
{
m_reportID = uint32(result->Fetch()[0].GetUInt64() + 1);
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(message_id) FROM mailbox");
if (result)
{
m_mailid = uint32(result->Fetch()[0].GetUInt64() + 1);
delete result;
}
result = CharacterDatabase.Query("SELECT MAX(setGUID) FROM equipmentsets");
if (result != nullptr)
{
m_setGUID = uint32(result->Fetch()[0].GetUInt32() + 1);
delete result;
}
#if VERSION_STRING > WotLK
result = CharacterDatabase.Query("SELECT MAX(itemId) FROM character_void_storage");
if (result != nullptr)
{
m_voidItemId = uint64_t(result->Fetch()[0].GetUInt32() + 1);
}
#endif
sLogger.info("ObjectMgr : HighGuid(CORPSE) = %lu", m_hiCorpseGuid.load());
sLogger.info("ObjectMgr : HighGuid(PLAYER) = %lu", m_hiPlayerGuid.load());
sLogger.info("ObjectMgr : HighGuid(GAMEOBJ) = %lu", m_hiGameObjectSpawnId.load());
sLogger.info("ObjectMgr : HighGuid(UNIT) = %lu", m_hiCreatureSpawnId.load());
sLogger.info("ObjectMgr : HighGuid(ITEM) = %lu", m_hiItemGuid.load());
sLogger.info("ObjectMgr : HighGuid(CONTAINER) = %lu", m_hiItemGuid.load());
sLogger.info("ObjectMgr : HighGuid(GROUP) = %lu", m_hiGroupId.load());
sLogger.info("ObjectMgr : HighGuid(CHARTER) = %lu", m_hiCharterId.load());
sLogger.info("ObjectMgr : HighGuid(GUILD) = %lu", m_hiGuildId.load());
sLogger.info("ObjectMgr : HighGuid(BUGREPORT) = %u", uint32(m_reportID.load() - 1));
sLogger.info("ObjectMgr : HighGuid(MAIL) = %u", uint32(m_mailid.load()));
sLogger.info("ObjectMgr : HighGuid(EQUIPMENTSET) = %u", uint32(m_setGUID.load() - 1));
}
uint32 ObjectMgr::GenerateReportID()
{
return ++m_reportID;
}
uint32 ObjectMgr::GenerateEquipmentSetID()
{
return ++m_setGUID;
}
uint32 ObjectMgr::GenerateMailID()
{
return ++m_mailid;
}
uint32 ObjectMgr::GenerateLowGuid(uint32 guidhigh)
{
uint32 ret;
switch (guidhigh)
{
case HIGHGUID_TYPE_PLAYER:
ret = ++m_hiPlayerGuid;
break;
case HIGHGUID_TYPE_ITEM:
case HIGHGUID_TYPE_CONTAINER:
ret = ++m_hiItemGuid;
break;
default:
sLogger.failure("ObjectMgr::GenerateLowGuid tried to generate low guid gor non player/item, return 0!");
ret = 0;
break;
}
return ret;
}
Player* ObjectMgr::GetPlayer(const char* name, bool caseSensitive)
{
std::lock_guard<std::mutex> guard(_playerslock);
if (!caseSensitive)
{
std::string strName = name;
AscEmu::Util::Strings::toLowerCase(strName);
for (PlayerStorageMap::const_iterator itr = _players.begin(); itr != _players.end(); ++itr)
{
if (!stricmp(itr->second->getName().c_str(), strName.c_str()))
{
return itr->second;
}
}
}
else
{
for (PlayerStorageMap::const_iterator itr = _players.begin(); itr != _players.end(); ++itr)
{
if (!strcmp(itr->second->getName().c_str(), name))
{
return itr->second;
}
}
}
return nullptr;
}
Player* ObjectMgr::GetPlayer(uint32 guid)
{
std::lock_guard<std::mutex> guard(_playerslock);
PlayerStorageMap::const_iterator itr = _players.find(guid);
return (itr != _players.end()) ? itr->second : nullptr;
}
void ObjectMgr::LoadVendors()
{
QueryResult* result = sMySQLStore.getWorldDBQuery("SELECT * FROM vendors");
if (result != nullptr)
{
std::unordered_map<uint32, std::vector<CreatureItem>*>::const_iterator itr;
std::vector<CreatureItem> *items;
if (result->GetFieldCount() < 6 + 1)
{
sLogger.failure("Invalid format in vendors (%u/6) columns, not enough data to proceed.", result->GetFieldCount());
delete result;
return;
}
else if (result->GetFieldCount() > 6 + 1)
{
sLogger.failure("Invalid format in vendors (%u/6) columns, loading anyway because we have enough data", result->GetFieldCount());
}
#if VERSION_STRING < Cata
DBC::Structures::ItemExtendedCostEntry const* item_extended_cost = nullptr;
#else
DB2::Structures::ItemExtendedCostEntry const* item_extended_cost = nullptr;
#endif
do
{
Field* fields = result->Fetch();
itr = mVendors.find(fields[0].GetUInt32());
if (itr == mVendors.end())
{
items = new std::vector < CreatureItem > ;
mVendors[fields[0].GetUInt32()] = items;
}
else
{
items = itr->second;
}
CreatureItem itm;
itm.itemid = fields[1].GetUInt32();
itm.amount = fields[2].GetUInt32();
itm.available_amount = fields[3].GetUInt32();
itm.max_amount = fields[3].GetUInt32();
itm.incrtime = fields[4].GetUInt32();
if (fields[5].GetUInt32() > 0)
{
item_extended_cost = sItemExtendedCostStore.LookupEntry(fields[5].GetUInt32());
if (item_extended_cost == nullptr)
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "LoadVendors : Extendedcost for item %u references nonexistent EC %u", fields[1].GetUInt32(), fields[5].GetUInt32());
}
else
item_extended_cost = nullptr;
itm.extended_cost = item_extended_cost;
items->push_back(itm);
}
while (result->NextRow());
delete result;
}
sLogger.info("ObjectMgr : %u vendors loaded.", static_cast<uint32_t>(mVendors.size()));
}
void ObjectMgr::ReloadVendors()
{
mVendors.clear();
LoadVendors();
}
std::vector<CreatureItem>* ObjectMgr::GetVendorList(uint32 entry)
{
return mVendors[entry];
}
Item* ObjectMgr::CreateItem(uint32 entry, Player* owner)
{
ItemProperties const* proto = sMySQLStore.getItemProperties(entry);
if (proto ==nullptr)
return nullptr;
if (proto->InventoryType == INVTYPE_BAG)
{
Container* pContainer = new Container(HIGHGUID_TYPE_CONTAINER, GenerateLowGuid(HIGHGUID_TYPE_CONTAINER));
pContainer->create(entry, owner);
pContainer->setStackCount(1);
return pContainer;
}
else
{
Item* pItem = new Item;
pItem->init(HIGHGUID_TYPE_ITEM, GenerateLowGuid(HIGHGUID_TYPE_ITEM));
pItem->create(entry, owner);
pItem->setStackCount(1);
#if VERSION_STRING > TBC
if (owner != nullptr)
{
uint32* played = owner->getPlayedTime();
pItem->setCreatePlayedTime(played[1]);
}
#endif
return pItem;
}
}
Item* ObjectMgr::LoadItem(uint32 lowguid)
{
QueryResult* result = CharacterDatabase.Query("SELECT * FROM playeritems WHERE guid = %u", lowguid);
Item* pReturn = nullptr;
if (result)
{
ItemProperties const* pProto = sMySQLStore.getItemProperties(result->Fetch()[2].GetUInt32());
if (!pProto)
return nullptr;
if (pProto->InventoryType == INVTYPE_BAG)
{
Container* pContainer = new Container(HIGHGUID_TYPE_CONTAINER, lowguid);
pContainer->loadFromDB(result->Fetch());
pReturn = pContainer;
}
else
{
Item* pItem = new Item;
pItem->init(HIGHGUID_TYPE_ITEM, lowguid);
pItem->loadFromDB(result->Fetch(), nullptr, false);
pReturn = pItem;
}
delete result;
}
return pReturn;
}
void ObjectMgr::GenerateLevelUpInfo()
{
struct MissingLevelData
{
uint32_t _level;
uint8_t _race;
uint8_t _class;
};
std::vector<MissingLevelData> _missingHealthLevelData;
std::vector<MissingLevelData> _missingStatLevelData;
// Copy existing level stats
uint32_t levelstat_counter = 0;
uint32_t class_levelstat_counter = 0;
for (uint8 Class = WARRIOR; Class < MAX_PLAYER_CLASSES; ++Class)
{
for (uint8 Race = RACE_HUMAN; Race < DBC_NUM_RACES; ++Race)
{
if (!isClassRaceCombinationPossible(Class, Race))
{
if (auto* playerLevelstats = sMySQLStore.getPlayerLevelstats(1, Race, Class))
{
sLogger.info("ObjectMgr : Invalid class/race combination! %u class and %u race.", uint32_t(Class), uint32_t(Race));
sLogger.info("ObjectMgr : But class/race values for level 1 in db!");
}
continue;
}
LevelMap* levelMap = new LevelMap;
for (uint32_t level = 1; level <= worldConfig.player.playerLevelCap; ++level)
{
LevelInfo* levelInfo = new LevelInfo;
if (auto* playerClassLevelstats = sMySQLStore.getPlayerClassLevelStats(level, Class))
{
levelInfo->HP = playerClassLevelstats->health;
levelInfo->Mana = playerClassLevelstats->mana;
++class_levelstat_counter;
}
else //calculate missing stats based on last level
{
levelInfo->HP = 0;
levelInfo->Mana = 0;
_missingHealthLevelData.push_back({ level, Race, Class });
}
if (auto* playerLevelstats = sMySQLStore.getPlayerLevelstats(level, Race, Class))
{
levelInfo->Stat[0] = playerLevelstats->strength;
levelInfo->Stat[1] = playerLevelstats->agility;
levelInfo->Stat[2] = playerLevelstats->stamina;
levelInfo->Stat[3] = playerLevelstats->intellect;
levelInfo->Stat[4] = playerLevelstats->spirit;
++levelstat_counter;
}
else //calculate missing stats based on last level
{
for (uint8_t id = 0; id < 5; ++id)
levelInfo->Stat[id] = 0;
_missingStatLevelData.push_back({ level, Race, Class });
}
// Insert into map
levelMap->insert(LevelMap::value_type(level, levelInfo));
}
// Insert back into the main map.
mLevelInfo.insert(LevelInfoMap::value_type(std::make_pair(Race, Class), levelMap));
}
}
sLogger.info("ObjectMgr : %u levelstats and %u classlevelstats applied from db.", levelstat_counter, class_levelstat_counter);
// generate missing data
uint32_t hp_counter = 0;
for (auto missingHP : _missingHealthLevelData)
{
uint32 TotalHealthGain = 0;
uint32 TotalManaGain = 0;
// use legacy gaining
switch (missingHP._class)
{
case WARRIOR:
if (missingHP._level < 13) TotalHealthGain += 19;
else if (missingHP._level < 36) TotalHealthGain += missingHP._level + 6;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 206;
else TotalHealthGain += 2 * missingHP._level - 30;
break;
case HUNTER:
if (missingHP._level < 13) TotalHealthGain += 17;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 161;
else TotalHealthGain += missingHP._level + 4;
if (missingHP._level < 11) TotalManaGain += 29;
else if (missingHP._level < 27) TotalManaGain += missingHP._level + 18;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 150;
else TotalManaGain += 45;
break;
case ROGUE:
if (missingHP._level < 15) TotalHealthGain += 17;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 191;
else TotalHealthGain += missingHP._level + 2;
break;
case DRUID:
if (missingHP._level < 17) TotalHealthGain += 17;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 176;
else TotalHealthGain += missingHP._level;
if (missingHP._level < 26) TotalManaGain += missingHP._level + 20;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 150;
else TotalManaGain += 45;
break;
case MAGE:
if (missingHP._level < 23) TotalHealthGain += 15;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 190;
else TotalHealthGain += missingHP._level - 8;
if (missingHP._level < 28) TotalManaGain += missingHP._level + 23;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 115;
else TotalManaGain += 51;
break;
case SHAMAN:
if (missingHP._level < 16) TotalHealthGain += 17;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 157;
else TotalHealthGain += missingHP._level + 1;
if (missingHP._level < 22) TotalManaGain += missingHP._level + 19;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 175;
else TotalManaGain += 49;
break;
case WARLOCK:
if (missingHP._level < 17) TotalHealthGain += 17;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 192;
else TotalHealthGain += missingHP._level - 2;
if (missingHP._level < 30) TotalManaGain += missingHP._level + 21;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 121;
else TotalManaGain += 51;
break;
case PALADIN:
if (missingHP._level < 14) TotalHealthGain += 18;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 167;
else TotalHealthGain += missingHP._level + 4;
if (missingHP._level < 30) TotalManaGain += missingHP._level + 17;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 131;
else TotalManaGain += 42;
break;
case PRIEST:
if (missingHP._level < 21) TotalHealthGain += 15;
else if (missingHP._level > 60) TotalHealthGain += missingHP._level + 157;
else TotalHealthGain += missingHP._level - 6;
if (missingHP._level < 22) TotalManaGain += missingHP._level + 22;
else if (missingHP._level < 32) TotalManaGain += missingHP._level + 37;
else if (missingHP._level > 60) TotalManaGain += missingHP._level + 207;
else TotalManaGain += 54;
break;
case DEATHKNIGHT:
TotalHealthGain += 92;
break;
default:
TotalHealthGain += 15;
TotalManaGain += 45;
break;
}
if (auto level_info = sObjectMgr.GetLevelInfo(missingHP._race, missingHP._class, missingHP._level))
{
level_info->HP = level_info->HP + TotalHealthGain;
level_info->Mana = level_info->Mana + TotalManaGain;
++hp_counter;
}
}
uint32_t stat_counter = 0;
for (auto missingStat : _missingStatLevelData)
{
if (auto level_info = sObjectMgr.GetLevelInfo(missingStat._race, missingStat._class, missingStat._level))
{
uint32 val;
for (uint8_t id = 0; id < 5; ++id)
{
val = GainStat(static_cast<uint16>(missingStat._level), missingStat._class, id);
level_info->Stat[id] = level_info->Stat[id] + val;
}
++stat_counter;
}
}
sLogger.info("ObjectMgr : %u level up information generated.", (stat_counter + hp_counter));
}
LevelInfo* ObjectMgr::GetLevelInfo(uint32 Race, uint32 Class, uint32 Level)
{
// Iterate levelinfo map until we find the right class+race.
LevelInfoMap::iterator itr = mLevelInfo.begin();
for (; itr != mLevelInfo.end(); ++itr)
{
if (itr->first.first == Race && itr->first.second == Class)
{
// We got a match.
// Let's check that our level is valid first.
if (Level > worldConfig.player.playerLevelCap)
Level = worldConfig.player.playerLevelCap;
// Pull the level information from the second map.
LevelMap::iterator it2 = itr->second->find(Level);
if (it2 == itr->second->end())
{
sLogger.info("GetLevelInfo : No level information found for level %u!", Level);
return nullptr;
}
return it2->second;
}
}
return nullptr;
}
void ObjectMgr::LoadPetSpellCooldowns()
{
for (uint32 i = 0; i < sCreatureSpellDataStore.GetNumRows(); ++i)
{
auto creture_spell_data = sCreatureSpellDataStore.LookupEntry(i);
for (uint8 j = 0; j < 3; ++j)
{
if (creture_spell_data == nullptr)
continue;
uint32 SpellId = creture_spell_data->Spells[j];
uint32 Cooldown = creture_spell_data->Cooldowns[j] * 10;
if (SpellId != 0)
{
PetSpellCooldownMap::iterator itr = mPetSpellCooldowns.find(SpellId);
if (itr == mPetSpellCooldowns.end())
{
if (Cooldown)
mPetSpellCooldowns.insert(std::make_pair(SpellId, Cooldown));
}
}
}
}
}
uint32 ObjectMgr::GetPetSpellCooldown(uint32 SpellId)
{
PetSpellCooldownMap::iterator itr = mPetSpellCooldowns.find(SpellId);
if (itr != mPetSpellCooldowns.end())
return itr->second;
SpellInfo const* sp = sSpellMgr.getSpellInfo(SpellId);
if (sp->getRecoveryTime() > sp->getCategoryRecoveryTime())
return sp->getRecoveryTime();
else
return sp->getCategoryRecoveryTime();
}
void ObjectMgr::SetVendorList(uint32 Entry, std::vector<CreatureItem>* list_)
{
mVendors[Entry] = list_;
}
void ObjectMgr::LoadCreatureTimedEmotes()
{
QueryResult* result = WorldDatabase.Query("SELECT * FROM creature_timed_emotes order by rowid asc");
if (!result)return;
do
{
Field* fields = result->Fetch();
spawn_timed_emotes* te = new spawn_timed_emotes;
te->type = fields[2].GetUInt8();
te->value = fields[3].GetUInt32();
char* str = (char*)fields[4].GetString();
if (str)
{
uint32 len = (int)strlen(str);
te->msg = new char[len + 1];
memcpy(te->msg, str, len + 1);
}
else te->msg = nullptr;
te->msg_type = static_cast<uint8>(fields[5].GetUInt32());
te->msg_lang = static_cast<uint8>(fields[6].GetUInt32());
te->expire_after = fields[7].GetUInt32();
std::unordered_map<uint32, TimedEmoteList*>::const_iterator i;
uint32 spawnid = fields[0].GetUInt32();
i = m_timedemotes.find(spawnid);
if (i == m_timedemotes.end())
{
TimedEmoteList* m = new TimedEmoteList;
m->push_back(te);
m_timedemotes[spawnid] = m;
}
else
{
i->second->push_back(te);
}
}
while (result->NextRow());
sLogger.info("ObjectMgr : %u timed emotes cached.", result->GetRowCount());
delete result;
}
TimedEmoteList* ObjectMgr::GetTimedEmoteList(uint32 spawnid)
{
std::unordered_map<uint32, TimedEmoteList*>::const_iterator i;
i = m_timedemotes.find(spawnid);
if (i != m_timedemotes.end())
{
TimedEmoteList* m = i->second;
return m;
}
else return nullptr;
}
Pet* ObjectMgr::CreatePet(uint32 entry)
{
uint32 guid;
guid = ++m_hiPetGuid;
return new Pet(WoWGuid(guid, entry, HIGHGUID_TYPE_PET));
}
Player* ObjectMgr::CreatePlayer(uint8 _class)
{
uint32_t guid= ++m_hiPlayerGuid;
return createPlayerByGuid(_class, guid);
}
void ObjectMgr::AddPlayer(Player* p)
{
std::lock_guard<std::mutex> guard(_playerslock);
_players[p->getGuidLow()] = p;
}
void ObjectMgr::RemovePlayer(Player* p)
{
std::lock_guard<std::mutex> guard(_playerslock);
_players.erase(p->getGuidLow());
}
void ObjectMgr::LoadReputationModifierTable(const char* tablename, ReputationModMap* dmap)
{
QueryResult* result = WorldDatabase.Query("SELECT * FROM %s", tablename);
if (result)
{
do
{
ReputationMod mod;
mod.faction[TEAM_ALLIANCE] = result->Fetch()[1].GetUInt32();
mod.faction[TEAM_HORDE] = result->Fetch()[2].GetUInt32();
mod.value = result->Fetch()[3].GetInt32();
mod.replimit = result->Fetch()[4].GetUInt32();
ReputationModMap::iterator itr = dmap->find(result->Fetch()[0].GetUInt32());
if (itr == dmap->end())
{
ReputationModifier* modifier = new ReputationModifier;
modifier->entry = result->Fetch()[0].GetUInt32();
modifier->mods.push_back(mod);
dmap->insert(ReputationModMap::value_type(result->Fetch()[0].GetUInt32(), modifier));
}
else
{
itr->second->mods.push_back(mod);
}
}
while (result->NextRow());
delete result;
}
sLogger.info("ObjectMgr : %u reputation modifiers on %s.", static_cast<uint32_t>(dmap->size()), tablename);
}
void ObjectMgr::LoadReputationModifiers()
{
LoadReputationModifierTable("reputation_creature_onkill", &m_reputation_creature);
LoadReputationModifierTable("reputation_faction_onkill", &m_reputation_faction);
LoadInstanceReputationModifiers();
}
ReputationModifier* ObjectMgr::GetReputationModifier(uint32 entry_id, uint32 faction_id)
{
// first, try fetching from the creature table (by faction is a fallback)
ReputationModMap::iterator itr = m_reputation_creature.find(entry_id);
if (itr != m_reputation_creature.end())
return itr->second;
// fetch from the faction table
itr = m_reputation_faction.find(faction_id);
if (itr != m_reputation_faction.end())
return itr->second;
// no data. fallback to default -5 value.
return nullptr;
}
void ObjectMgr::LoadInstanceReputationModifiers()
{
QueryResult* result = WorldDatabase.Query("SELECT * FROM reputation_instance_onkill");
if (!result)
return;
do
{
Field* fields = result->Fetch();
InstanceReputationMod mod;
mod.mapid = fields[0].GetUInt32();
mod.mob_rep_reward = fields[1].GetInt32();
mod.mob_rep_limit = fields[2].GetUInt32();
mod.boss_rep_reward = fields[3].GetInt32();
mod.boss_rep_limit = fields[4].GetUInt32();
mod.faction[TEAM_ALLIANCE] = fields[5].GetUInt32();
mod.faction[TEAM_HORDE] = fields[6].GetUInt32();
std::unordered_map<uint32, InstanceReputationModifier*>::iterator itr = m_reputation_instance.find(mod.mapid);
if (itr == m_reputation_instance.end())
{
InstanceReputationModifier* m = new InstanceReputationModifier;
m->mapid = mod.mapid;
m->mods.push_back(mod);
m_reputation_instance.insert(std::make_pair(m->mapid, m));
}
else
itr->second->mods.push_back(mod);
}
while (result->NextRow());
delete result;
sLogger.info("ObjectMgr : %u instance reputation modifiers loaded.", static_cast<uint32_t>(m_reputation_instance.size()));
}
bool ObjectMgr::HandleInstanceReputationModifiers(Player* pPlayer, Unit* pVictim)
{
uint32 team = pPlayer->getTeam();
if (!pVictim->isCreature())
return false;
std::unordered_map<uint32, InstanceReputationModifier*>::iterator itr = m_reputation_instance.find(pVictim->GetMapId());
if (itr == m_reputation_instance.end())
return false;
bool is_boss = false;
if (static_cast< Creature* >(pVictim)->GetCreatureProperties()->isBoss)
is_boss = true;
// Apply the bonuses as normal.
int32 replimit;
int32 value;
for (std::vector<InstanceReputationMod>::iterator i = itr->second->mods.begin(); i != itr->second->mods.end(); ++i)
{
if (!(*i).faction[team])
continue;
if (is_boss)
{
value = i->boss_rep_reward;
replimit = i->boss_rep_limit;
}
else
{
value = i->mob_rep_reward;
replimit = i->mob_rep_limit;
}
if (!value || (replimit && pPlayer->getFactionStanding(i->faction[team]) >= replimit))
continue;
//value *= sWorld.getRate(RATE_KILLREPUTATION);
value = float2int32(value * worldConfig.getFloatRate(RATE_KILLREPUTATION));
pPlayer->modFactionStanding(i->faction[team], value);
}
return true;
}
void ObjectMgr::LoadGroups()
{
QueryResult* result = CharacterDatabase.Query("SELECT * FROM `groups`");
if (result)
{
if (result->GetFieldCount() != 52)
{
sLogger.failure("groups table format is invalid. Please update your database.");
return;
}
do
{
Group* g = new Group(false);
g->LoadFromDB(result->Fetch());
}
while (result->NextRow());
delete result;
}
sLogger.info("ObjectMgr : %u groups loaded.", static_cast<uint32_t>(this->m_groups.size()));
}
void ObjectMgr::loadGroupInstances()
{
// Delete Invalid Instances
CharacterDatabase.Execute("DELETE FROM group_instance WHERE guid NOT IN (SELECT guid FROM `groups`)");
QueryResult* result = CharacterDatabase.Query("SELECT gi.guid, i.map, gi.instance, gi.permanent, i.difficulty, i.resettime, (SELECT COUNT(1) FROM character_instance ci LEFT JOIN `groups` g ON ci.guid = g.group1member1 WHERE ci.instance = gi.instance AND ci.permanent = 1 LIMIT 1) FROM group_instance gi LEFT JOIN instance i ON gi.instance = i.id ORDER BY guid");
if (!result)
{
sLogger.info("Loaded 0 group-instance saves. DB table `group_instance` is empty!");
return;
}
uint32_t count = 0;
do
{
Field* fields = result->Fetch();
Group* group = sObjectMgr.GetGroupById(fields[0].GetUInt32());
DBC::Structures::MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt16());
if (!mapEntry || !mapEntry->isDungeon())
{
sLogger.failure("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt16());
continue;
}
uint32_t diff = fields[4].GetUInt8();
if (diff >= static_cast<uint32_t>(mapEntry->isRaid() ? InstanceDifficulty::Difficulties::MAX_RAID_DIFFICULTY : InstanceDifficulty::Difficulties::MAX_DUNGEON_DIFFICULTY))
{
sLogger.failure("Wrong dungeon difficulty use in group_instance table: %d", diff + 1);
diff = 0; // default for both difficaly types
}
InstanceSaved* save = sInstanceMgr.addInstanceSave(mapEntry->id, fields[2].GetUInt32(), InstanceDifficulty::Difficulties(diff), time_t(fields[5].GetUInt64()), fields[6].GetUInt64() == 0, true);
group->bindToInstance(save, fields[3].GetBool(), true);
++count;
}
while (result->NextRow());
delete result;
sLogger.info("Loaded %u group-instance saves", count);
}
void ObjectMgr::ResetDailies()
{
std::lock_guard<std::mutex> guard(_playerslock);
for (auto itr : _players)
{
if (Player* pPlayer = itr.second)
pPlayer->resetFinishedDailies();
}
}
uint32 ObjectMgr::GenerateArenaTeamId()
{
uint32 ret;
ret = ++m_hiArenaTeamId;
return ret;
}
uint32 ObjectMgr::GenerateGroupId()
{
uint32 r;
r = ++m_hiGroupId;
return r;
}
uint32 ObjectMgr::GenerateGuildId()
{
uint32 r;
r = ++m_hiGuildId;
return r;
}
void ObjectMgr::AddGroup(Group* group)
{
std::lock_guard<std::mutex> guard(m_groupLock);
m_groups.insert(std::make_pair(group->GetID(), group));
}
void ObjectMgr::RemoveGroup(Group* group)
{
std::lock_guard<std::mutex> guard(m_groupLock);
m_groups.erase(group->GetID());
}
uint32 ObjectMgr::GenerateCreatureSpawnID()
{
uint32 r;
r = ++m_hiCreatureSpawnId;
return r;
}
uint32 ObjectMgr::GenerateGameObjectSpawnID()
{
uint32 r;
r = ++m_hiGameObjectSpawnId;
return r;
}
#ifdef FT_VEHICLES
void ObjectMgr::LoadVehicleAccessories()
{
_vehicleAccessoryStore.clear();
QueryResult* result = WorldDatabase.Query("SELECT entry, accessory_entry, seat_id , minion, summontype, summontimer FROM vehicle_accessories;");
if (result != nullptr)
{
do
{
Field* fields = result->Fetch();
uint32_t entry = fields[0].GetUInt32();
uint32_t accessory = fields[1].GetUInt32();
int8_t seatId = fields[2].GetInt8();
bool isMinion = fields[3].GetBool();
uint8_t summonType = fields[4].GetUInt8();
uint32_t summonTimer = fields[5].GetUInt32();
if (!sMySQLStore.getCreatureProperties(entry))
{
sLogger.failure("Table `vehicle_accessories`: creature template entry %u does not exist.", entry);
continue;
}
if (!sMySQLStore.getCreatureProperties(accessory))
{
sLogger.failure("Table `vehicle_accessories`: Accessory %u does not exist.", accessory);
continue;
}
auto _spellClickInfoStore = sMySQLStore.getSpellClickSpellsStore();
if (_spellClickInfoStore->find(entry) == _spellClickInfoStore->end())
{
sLogger.failure("Table `vehicle_accessories`: creature template entry %u has no data in npc_spellclick_spells", entry);
continue;
}
_vehicleAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer));
} while (result->NextRow());
delete result;
}
}
void ObjectMgr::loadVehicleSeatAddon()
{
_vehicleSeatAddonStore.clear();
QueryResult* result = WorldDatabase.Query("SELECT SeatEntry, SeatOrientation, ExitParamX , ExitParamY, ExitParamZ, ExitParamO, ExitParamValue FROM vehicle_seat_addon;");
if (result != nullptr)
{
do
{
Field* fields = result->Fetch();
uint32_t seatID = fields[0].GetUInt32();
float orientation = fields[1].GetFloat();
float exitX = fields[2].GetFloat();
float exitY = fields[3].GetFloat();
float exitZ = fields[4].GetFloat();
float exitO = fields[5].GetFloat();
uint8_t exitParam = fields[6].GetUInt8();
_vehicleSeatAddonStore[seatID] = VehicleSeatAddon(orientation, { exitX, exitY, exitZ, exitO }, exitParam);
} while (result->NextRow());
delete result;
}
}
VehicleAccessoryList const* ObjectMgr::getVehicleAccessories(Vehicle* vehicle)
{
VehicleAccessoryContainer::const_iterator itr = _vehicleAccessoryStore.find(vehicle->getEntry());
if (itr != _vehicleAccessoryStore.end())
return &itr->second;
return nullptr;
}
#endif
void ObjectMgr::LoadEventScripts()
{
sLogger.info("ObjectMgr : Loading Event Scripts...");
bool success = false;
const char* eventScriptsQuery = "SELECT `event_id`, `function`, `script_type`, `data_1`, `data_2`, `data_3`, `data_4`, `data_5`, `x`, `y`, `z`, `o`, `delay`, `next_event` FROM `event_scripts` WHERE `event_id` > 0 ORDER BY `event_id`";
auto result = WorldDatabase.Query(&success, eventScriptsQuery);
if (!success)
{
sLogger.debug("LoadEventScripts : Failed on Loading Queries from event_scripts.");
return;
}
else
{
if (!result)
{
sLogger.debug("LoadEventScripts : Loaded 0 event_scripts. DB table `event_scripts` is empty.");
return;
}
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint32 event_id = fields[0].GetUInt32();
SimpleEventScript eventscript;
eventscript.eventId = event_id;
eventscript.function = static_cast<uint8>(ScriptCommands(fields[1].GetUInt8()));
eventscript.scripttype = static_cast<uint8>(EasyScriptTypes(fields[2].GetUInt8()));
eventscript.data_1 = fields[3].GetUInt32();
eventscript.data_2 = fields[4].GetUInt32();
eventscript.data_3 = fields[5].GetUInt32();
eventscript.data_4 = fields[6].GetUInt32();
eventscript.data_5 = fields[7].GetUInt32();
eventscript.x = fields[8].GetUInt32();
eventscript.y = fields[9].GetUInt32();
eventscript.z = fields[10].GetUInt32();
eventscript.o = fields[11].GetUInt32();
eventscript.delay = fields[12].GetUInt32();
eventscript.nextevent = fields[13].GetUInt32();
SimpleEventScript* SimpleEventScript = &mEventScriptMaps.insert(EventScriptMaps::value_type(event_id, eventscript))->second;
// for search by spellid ( data_1 is spell id )
if (eventscript.data_1 && eventscript.scripttype == static_cast<uint8>(EasyScriptTypes::SCRIPT_TYPE_SPELL_EFFECT))
mSpellEffectMaps.insert(SpellEffectMaps::value_type(eventscript.data_1, SimpleEventScript));
++count;
} while (result->NextRow());
delete result;
sLogger.info("ObjectMgr : Loaded event_scripts for %u events...", count);
}
EventScriptBounds ObjectMgr::GetEventScripts(uint32 event_id) const
{
return EventScriptBounds(mEventScriptMaps.lower_bound(event_id), mEventScriptMaps.upper_bound(event_id));
}
SpellEffectMapBounds ObjectMgr::GetSpellEffectBounds(uint32 data_1) const
{
return SpellEffectMapBounds(mSpellEffectMaps.lower_bound(data_1), mSpellEffectMaps.upper_bound(data_1));
}
bool ObjectMgr::CheckforScripts(Player* plr, uint32 event_id)
{
EventScriptBounds EventScript = sObjectMgr.GetEventScripts(event_id);
if (EventScript.first == EventScript.second)
return false;
for (EventScriptMaps::const_iterator itr = EventScript.first; itr != EventScript.second; ++itr)
{
sEventMgr.AddEvent(this, &ObjectMgr::EventScriptsUpdate, plr, itr->second.eventId, EVENT_EVENT_SCRIPTS, itr->second.delay, 1, 0);
}
return true;
}
bool ObjectMgr::CheckforDummySpellScripts(Player* plr, uint32 data_1)
{
SpellEffectMapBounds EventScript = sObjectMgr.GetSpellEffectBounds(data_1);
if (EventScript.first == EventScript.second)
return false;
for (SpellEffectMaps::const_iterator itr = EventScript.first; itr != EventScript.second; ++itr)
{
sEventMgr.AddEvent(this, &ObjectMgr::EventScriptsUpdate, plr, itr->second->eventId, EVENT_EVENT_SCRIPTS, itr->second->delay, 1, 0);
}
return true;
}
void ObjectMgr::EventScriptsUpdate(Player* plr, uint32 next_event)
{
EventScriptBounds EventScript = sObjectMgr.GetEventScripts(next_event);
for (EventScriptMaps::const_iterator itr = EventScript.first; itr != EventScript.second; ++itr)
{
if (itr->second.scripttype == static_cast<uint8>(EasyScriptTypes::SCRIPT_TYPE_SPELL_EFFECT) || itr->second.scripttype == static_cast<uint8>(EasyScriptTypes::SCRIPT_TYPE_DUMMY))
{
switch (itr->second.function)
{
case static_cast<uint8>(ScriptCommands::SCRIPT_COMMAND_RESPAWN_GAMEOBJECT):
{
Object* target = plr->getWorldMap()->getInterface()->getGameObjectNearestCoords(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), itr->second.data_1);
if (target == nullptr)
return;
static_cast<GameObject*>(target)->despawn(1000, itr->second.data_2);
break;
}
case static_cast<uint8>(ScriptCommands::SCRIPT_COMMAND_KILL_CREDIT):
{
if (auto* questLog = plr->getQuestLogByQuestId(itr->second.data_2))
{
if (questLog->getQuestProperties()->required_mob_or_go[itr->second.data_5] >= 0)
{
uint32 required_mob = static_cast<uint32>(questLog->getQuestProperties()->required_mob_or_go[itr->second.data_5]);
const auto index = static_cast<uint8_t>(itr->second.data_5);
if (questLog->getMobCountByIndex(index) < required_mob)
{
questLog->setMobCountForIndex(index, questLog->getMobCountByIndex(index) + 1);
questLog->sendUpdateAddKill(index);
questLog->updatePlayerFields();
}
}
}
break;
}
}
}
if (itr->second.scripttype == static_cast<uint8>(EasyScriptTypes::SCRIPT_TYPE_GAMEOBJECT) || itr->second.scripttype == static_cast<uint8>(EasyScriptTypes::SCRIPT_TYPE_DUMMY))
{
switch (itr->second.function)
{
case static_cast<uint8>(ScriptCommands::SCRIPT_COMMAND_ACTIVATE_OBJECT):
{
if ((itr->second.x || itr->second.y || itr->second.z) == 0)
{
Object* target = plr->getWorldMap()->getInterface()->getGameObjectNearestCoords(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), itr->second.data_1);
if (target == nullptr)
return;
if (static_cast<GameObject*>(target)->getState() != GO_STATE_OPEN)
{
static_cast<GameObject*>(target)->setState(GO_STATE_OPEN);
}
else
{
static_cast<GameObject*>(target)->setState(GO_STATE_CLOSED);
}
}
else
{
Object* target = plr->getWorldMap()->getInterface()->getGameObjectNearestCoords(float(itr->second.x), float(itr->second.y), float(itr->second.z), itr->second.data_1);
if (target == nullptr)
return;
if (static_cast<GameObject*>(target)->getState() != GO_STATE_OPEN)
{
static_cast<GameObject*>(target)->setState(GO_STATE_OPEN);
}
else
{
static_cast<GameObject*>(target)->setState(GO_STATE_CLOSED);
}
}
}
break;
}
}
if (itr->second.nextevent != 0)
{
sObjectMgr.CheckforScripts(plr, itr->second.nextevent);
}
}
}
void ObjectMgr::LoadInstanceEncounters()
{
const auto startTime = Util::TimeNow();
// 0 1 2 3 4 5
QueryResult* result = WorldDatabase.Query("SELECT entry, creditType, creditEntry, lastEncounterDungeon, comment, mapid FROM instance_encounters");
if (result == nullptr)
{
sLogger.debug(">> Loaded 0 instance encounters, table is empty!");
return;
}
#if VERSION_STRING >= WotLK
std::map<uint32_t, DBC::Structures::DungeonEncounterEntry const*> dungeonLastBosses;
#endif
uint32_t count = 0;
do
{
Field* fields = result->Fetch();
auto entry = fields[0].GetUInt32();
auto creditType = fields[1].GetUInt8();
auto creditEntry = fields[2].GetUInt32();
auto lastEncounterDungeon = fields[3].GetUInt16();
auto dungeonEncounterName = fields[4].GetString();
#if VERSION_STRING <= TBC
auto mapId = fields[5].GetUInt32();
#else
const auto dungeonEncounter = sDungeonEncounterStore.LookupEntry(entry);
if (dungeonEncounter == nullptr)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "Table `instance_encounters` has an invalid encounter id %u, skipped!", entry);
continue;
}
#if VERSION_STRING == WotLK
dungeonEncounterName = dungeonEncounter->encounterName[sWorld.getDbcLocaleLanguageId()];
#else
dungeonEncounterName = dungeonEncounter->encounterName;
#endif
#endif
if (lastEncounterDungeon && sLfgMgr.GetLFGDungeon(lastEncounterDungeon) == 0)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounterName, lastEncounterDungeon);
continue;
}
#if VERSION_STRING >= WotLK
if (lastEncounterDungeon)
{
const auto itr = dungeonLastBosses.find(lastEncounterDungeon);
if (itr != dungeonLastBosses.end())
{
#if VERSION_STRING == WotLK
const auto itrEncounterName = itr->second->encounterName[sWorld.getDbcLocaleLanguageId()];
#else
const auto itrEncounterName = itr->second->encounterName;
#endif
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "Table `instance_encounters` specified encounter %u (%s) as last encounter but %u (%s) is already marked as one, skipped!", entry, dungeonEncounterName, itr->second->id, itrEncounterName);
continue;
}
dungeonLastBosses[lastEncounterDungeon] = dungeonEncounter;
}
#endif
switch (creditType)
{
case ENCOUNTER_CREDIT_KILL_CREATURE:
{
auto creatureprop = sMySQLStore.getCreatureProperties(creditEntry);
if (creatureprop == nullptr)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "Table `instance_encounters` has an invalid creature (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounterName);
continue;
}
const_cast<CreatureProperties*>(creatureprop)->extra_a9_flags |= 0x10000000; // Flagged Dungeon Boss
break;
}
case ENCOUNTER_CREDIT_CAST_SPELL:
{
if (sSpellMgr.getSpellInfo(creditEntry) == nullptr)
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "Table `instance_encounters` has an invalid spell (entry %u) linked to the encounter %u (%s), skipped!", creditEntry, entry, dungeonEncounterName);
continue;
}
break;
}
default:
{
sLogger.debugFlag(AscEmu::Logging::LF_DB_TABLES, "Table `instance_encounters` has an invalid credit type (%u) for encounter %u (%s), skipped!", creditType, entry, dungeonEncounterName);
continue;
}
}
#if VERSION_STRING <= TBC
DungeonEncounterList& encounters = _dungeonEncounterStore[mapId];
encounters.push_back(std::make_shared<DungeonEncounter>(EncounterCreditType(creditType), creditEntry));
#else
DungeonEncounterList& encounters = m_dungeonEncounterStore[static_cast<int32_t>(static_cast<uint16_t>(dungeonEncounter->mapId) | (static_cast<uint32_t>(dungeonEncounter->difficulty) << 16))];
encounters.push_back(std::make_shared<DungeonEncounter>(dungeonEncounter, EncounterCreditType(creditType), creditEntry, lastEncounterDungeon));
#endif
++count;
} while (result->NextRow());
sLogger.info("ObjectMgr : Loaded %u instance encounters in %u ms", count, static_cast<uint32_t>(Util::GetTimeDifferenceToNow(startTime)));
}
#if VERSION_STRING > WotLK
uint64_t ObjectMgr::generateVoidStorageItemId()
{
return ++m_voidItemId;
}
#endif