feat(CORE/Instance): access_requirement db refactor and improved output (#3696)

This commit is contained in:
Petric
2021-03-29 17:24:52 +01:00
committed by GitHub
parent c0aa1b88e8
commit f11d3a5402
11 changed files with 807 additions and 138 deletions

View File

@@ -391,6 +391,7 @@ namespace lfg
if (!dungeon) // should never happen - We provide a list from sLFGDungeonStore
continue;
MapEntry const* mapEntry = sMapStore.LookupEntry(dungeon->map);
DungeonProgressionRequirements const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty));
uint32 lockData = 0;
if (dungeon->expansion > expansion)
@@ -401,29 +402,58 @@ namespace lfg
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && (!mapEntry || !mapEntry->IsRaid()) && sInstanceSaveMgr->PlayerIsPermBoundToInstance(player->GetGUIDLow(), dungeon->map, Difficulty(dungeon->difficulty)))
lockData = LFG_LOCKSTATUS_RAID_LOCKED;
else if (dungeon->minlevel > level)
else if ((dungeon->minlevel > level && !sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE)) || (sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE) && ar && ar->levelMin > 0 && ar->levelMin > level))
lockData = LFG_LOCKSTATUS_TOO_LOW_LEVEL;
else if (dungeon->maxlevel < level)
else if ((dungeon->maxlevel < level && !sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE)) || (sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE) && ar && ar->levelMax > 0 && ar->levelMax < level))
lockData = LFG_LOCKSTATUS_TOO_HIGH_LEVEL;
else if (dungeon->seasonal && !IsSeasonActive(dungeon->id))
lockData = LFG_LOCKSTATUS_NOT_IN_SEASON;
else if (AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty)))
else if (ar)
{
if (ar->achievement && !player->HasAchieved(ar->achievement))
lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT;
else if (ar->reqItemLevel && (float)ar->reqItemLevel > avgItemLevel)
lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
else if (player->GetTeamId() == TEAM_ALLIANCE && ar->quest_A && !player->GetQuestRewardStatus(ar->quest_A))
lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED;
else if (player->GetTeamId() == TEAM_HORDE && ar->quest_H && !player->GetQuestRewardStatus(ar->quest_H))
lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED;
else if (ar->item)
// Check required items
for (const ProgressionRequirement* itemRequirement : ar->items)
{
if (!player->HasItemCount(ar->item) && (!ar->item2 || !player->HasItemCount(ar->item2)))
lockData = LFG_LOCKSTATUS_MISSING_ITEM;
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == player->GetTeamId(true))
{
if (!player->HasItemCount(itemRequirement->id, 1))
{
lockData = LFG_LOCKSTATUS_MISSING_ITEM;
break;
}
}
}
//Check for quests
for (const ProgressionRequirement* questRequirement : ar->quests)
{
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == player->GetTeamId(true))
{
if (!player->GetQuestRewardStatus(questRequirement->id))
{
lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED;
break;
}
}
}
//Check for ilvl
if (ar->reqItemLevel && (float)ar->reqItemLevel > avgItemLevel)
{
lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE;
}
//Check if player has the required achievements
for (const ProgressionRequirement* achievementRequirement : ar->achievements)
{
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == player->GetTeamId(true))
{
if (!player->HasAchieved(achievementRequirement->id))
{
lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT;
break;
}
}
}
else if (ar->item2 && !player->HasItemCount(ar->item2))
lockData = LFG_LOCKSTATUS_MISSING_ITEM;
}
sScriptMgr->OnInitializeLockedDungeons(player, level, lockData);

View File

@@ -19639,7 +19639,115 @@ void Player::SendSavedInstances()
}
}
bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report)
void Player::PrettyPrintRequirementsQuestList(const std::vector<const ProgressionRequirement*>& missingQuests) const
{
LocaleConstant loc_idx = GetSession()->GetSessionDbLocaleIndex();
for (const ProgressionRequirement* missingReq : missingQuests)
{
Quest const* questTemplate = sObjectMgr->GetQuestTemplate(missingReq->id);
if (!questTemplate)
{
continue;
}
std::string questTitle = questTemplate->GetTitle();
if (QuestLocale const* questLocale = sObjectMgr->GetQuestLocale(questTemplate->GetQuestId()))
{
ObjectMgr::GetLocaleString(questLocale->Title, loc_idx, questTitle);
}
std::stringstream stream;
stream << "|cffff7c0a|Hquest:";
stream << questTemplate->GetQuestId();
stream << ":";
stream << questTemplate->GetQuestLevel();
stream << "|h[";
stream << questTitle;
stream << "]|h|r";
if (missingReq->note.empty())
{
ChatHandler(GetSession()).PSendSysMessage(" - %s", stream.str().c_str());
}
else
{
ChatHandler(GetSession()).PSendSysMessage(" - %s %s %s", stream.str().c_str(), sObjectMgr->GetAcoreString(LANG_ACCESS_REQUIREMENT_NOTE, loc_idx), missingReq->note.c_str());
}
}
}
void Player::PrettyPrintRequirementsAchievementsList(const std::vector<const ProgressionRequirement*>& missingAchievements) const
{
LocaleConstant loc_idx = GetSession()->GetSessionDbLocaleIndex();
for (const ProgressionRequirement* missingReq : missingAchievements)
{
AchievementEntry const* achievementEntry = sAchievementStore.LookupEntry(missingReq->id);
if (!achievementEntry)
{
continue;
}
std::string name = *achievementEntry->name;
std::stringstream stream;
stream << "|cffff7c0a|Hachievement:";
stream << missingReq->id;
stream << ":";
stream << std::hex << GetGUID() << std::dec;
stream << ":0:0:0:0:0:0:0:0|h[";
stream << name;
stream << "]|h|r";
if (missingReq->note.empty())
{
ChatHandler(GetSession()).PSendSysMessage(" - %s", stream.str().c_str());
}
else
{
ChatHandler(GetSession()).PSendSysMessage(" - %s %s %s", stream.str().c_str(), sObjectMgr->GetAcoreString(LANG_ACCESS_REQUIREMENT_NOTE, loc_idx), missingReq->note.c_str());
}
}
}
void Player::PrettyPrintRequirementsItemsList(const std::vector<const ProgressionRequirement*>& missingItems) const
{
LocaleConstant loc_idx = GetSession()->GetSessionDbLocaleIndex();
for (const ProgressionRequirement* missingReq : missingItems)
{
const ItemTemplate* itemTemplate = sObjectMgr->GetItemTemplate(missingReq->id);
if (!itemTemplate)
{
continue;
}
//Get the localised name
std::string name = itemTemplate->Name1;
if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemTemplate->ItemId))
{
ObjectMgr::GetLocaleString(il->Name, loc_idx, name);
}
std::stringstream stream;
stream << "|c";
stream << std::hex << ItemQualityColors[itemTemplate->Quality] << std::dec;
stream << "|Hitem:";
stream << itemTemplate->ItemId;
stream << ":0:0:0:0:0:0:0:0:0|h[";
stream << name;
stream << "]|h|r";
if (missingReq->note.empty())
{
ChatHandler(GetSession()).PSendSysMessage(" - %s", stream.str().c_str());
}
else
{
ChatHandler(GetSession()).PSendSysMessage(" - %s %s %s", stream.str().c_str(), sObjectMgr->GetAcoreString(LANG_ACCESS_REQUIREMENT_NOTE, loc_idx), missingReq->note.c_str());
}
}
}
bool Player::Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map, bool report)
{
if (!IsGameMaster() && ar)
{
@@ -19658,52 +19766,214 @@ bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report
LevelMax = ar->levelMax;
}
uint32 missingItem = 0;
if (ar->item)
{
if (!HasItemCount(ar->item, 1) &&
(!ar->item2 || !HasItemCount(ar->item2)))
missingItem = ar->item;
}
else if (ar->item2 && !HasItemCount(ar->item2))
missingItem = ar->item2;
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, target_map, this))
{
GetSession()->SendAreaTriggerMessage("%s", GetSession()->GetAcoreString(LANG_INSTANCE_CLOSED));
return false;
}
uint32 missingQuest = 0;
if (GetTeamId(true) == TEAM_ALLIANCE && ar->quest_A && !GetQuestRewardStatus(ar->quest_A))
missingQuest = ar->quest_A;
else if (GetTeamId(true) == TEAM_HORDE && ar->quest_H && !GetQuestRewardStatus(ar->quest_H))
missingQuest = ar->quest_H;
Player* partyLeader = this;
std::string leaderName = m_session->GetAcoreString(LANG_YOU);
{
const uint64 leaderGuid = GetGroup() ? GetGroup()->GetLeaderGUID() : GetGUID();
Player* tempLeader = HashMapHolder<Player>::Find(leaderGuid);
uint32 missingAchievement = 0;
Player* leader = this;
uint64 leaderGuid = GetGroup() ? GetGroup()->GetLeaderGUID() : GetGUID();
if (leaderGuid != GetGUID())
leader = HashMapHolder<Player>::Find(leaderGuid);
if (leaderGuid != GetGUID())
{
if (tempLeader != nullptr)
{
partyLeader = tempLeader;
}
leaderName = GetGroup()->GetLeaderName();
}
}
//Check all items
std::vector<const ProgressionRequirement*> missingPlayerItems;
std::vector<const ProgressionRequirement*> missingLeaderItems;
for (const ProgressionRequirement* itemRequirement : ar->items)
{
Player* checkPlayer = this;
std::vector<const ProgressionRequirement*>* missingItems = &missingPlayerItems;
if (itemRequirement->checkLeaderOnly)
{
checkPlayer = partyLeader;
missingItems = &missingLeaderItems;
}
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == checkPlayer->GetTeamId(true))
{
if (!checkPlayer->HasItemCount(itemRequirement->id, 1))
{
missingItems->push_back(itemRequirement);
}
}
}
//Check all achievements
std::vector<const ProgressionRequirement*> missingPlayerAchievements;
std::vector<const ProgressionRequirement*> missingLeaderAchievements;
for (const ProgressionRequirement* achievementRequirement : ar->achievements)
{
Player* checkPlayer = this;
std::vector<const ProgressionRequirement*>* missingAchievements = &missingPlayerAchievements;
if(achievementRequirement->checkLeaderOnly)
{
checkPlayer = partyLeader;
missingAchievements = &missingLeaderAchievements;
}
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == GetTeamId(true))
{
if (!checkPlayer || !checkPlayer->HasAchieved(achievementRequirement->id))
{
missingAchievements->push_back(achievementRequirement);
}
}
}
//Check all quests
std::vector<const ProgressionRequirement*> missingPlayerQuests;
std::vector<const ProgressionRequirement*> missingLeaderQuests;
for (const ProgressionRequirement* questRequirement : ar->quests)
{
Player* checkPlayer = this;
std::vector<const ProgressionRequirement*>* missingQuests = &missingPlayerQuests;
if (questRequirement->checkLeaderOnly)
{
checkPlayer = partyLeader;
missingQuests = &missingLeaderQuests;
}
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == checkPlayer->GetTeamId(true))
{
if (!checkPlayer->GetQuestRewardStatus(questRequirement->id))
{
missingQuests->push_back(questRequirement);
}
}
}
//Check if avg ILVL requirement is allowed
bool ilvlRequirementNotMet = false;
if (sWorld->getBoolConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL))
{
const int32 currentIlvl = (int32)GetAverageItemLevelForDF();
if (ar->reqItemLevel > currentIlvl)
{
ilvlRequirementNotMet = true;
}
}
if (ar->achievement)
if (!leader || !leader->HasAchieved(ar->achievement))
missingAchievement = ar->achievement;
Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid());
MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(target_map, target_difficulty);
if (LevelMin || LevelMax || missingItem || missingQuest || missingAchievement)
if (LevelMin || LevelMax || ilvlRequirementNotMet
|| missingPlayerItems.size() || missingPlayerQuests.size() || missingPlayerAchievements.size()
|| missingLeaderItems.size() || missingLeaderQuests.size() || missingLeaderAchievements.size())
{
if (report)
{
if (missingQuest && !ar->questFailedText.empty())
ChatHandler(GetSession()).PSendSysMessage("%s", ar->questFailedText.c_str());
else if (mapDiff->hasErrorMessage) // if (missingAchievement) covered by this case
SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty);
else if (missingItem)
GetSession()->SendAreaTriggerMessage(GetSession()->GetAcoreString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, sObjectMgr->GetItemTemplate(missingItem)->Name1.c_str());
else if (LevelMin)
GetSession()->SendAreaTriggerMessage(GetSession()->GetAcoreString(LANG_LEVEL_MINREQUIRED), LevelMin);
uint8 requirementPrintMode = sWorld->getIntConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE);
if (requirementPrintMode == 0)
{
//Just print out the requirements are not met
ChatHandler(GetSession()).SendSysMessage(LANG_ACCESS_REQUIREMENT_NOT_MET);
}
else if(requirementPrintMode == 1)
{
//Blizzlike method of printing out the requirements
if (missingLeaderQuests.size() && !missingLeaderQuests[0]->note.empty())
{
ChatHandler(GetSession()).PSendSysMessage("%s", missingLeaderQuests[0]->note.c_str());
}
else if (mapDiff->hasErrorMessage)
{ // if (missingAchievement) covered by this case
SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty);
}
else if (missingPlayerItems.size())
{
GetSession()->SendAreaTriggerMessage(GetSession()->GetAcoreString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, sObjectMgr->GetItemTemplate(missingPlayerItems[0]->id)->Name1.c_str());
}
else if (LevelMin)
{
GetSession()->SendAreaTriggerMessage(GetSession()->GetAcoreString(LANG_LEVEL_MINREQUIRED), LevelMin);
}
else if (ilvlRequirementNotMet)
{
ChatHandler(GetSession()).PSendSysMessage(LANG_ACCESS_REQUIREMENT_AVERAGE_ILVL_NOT_MET, ar->reqItemLevel, (uint16)GetAverageItemLevelForDF());
}
}
else
{
bool errorAlreadyPrinted = false;
//Pretty way of printing out requirements
if (missingPlayerQuests.size())
{
ChatHandler(GetSession()).SendSysMessage(LANG_ACCESS_REQUIREMENT_COMPLETE_QUESTS);
PrettyPrintRequirementsQuestList(missingPlayerQuests);
errorAlreadyPrinted = true;
}
if (missingLeaderQuests.size())
{
ChatHandler(GetSession()).PSendSysMessage(LANG_ACCESS_REQUIREMENT_LEADER_COMPLETE_QUESTS, leaderName.c_str());
PrettyPrintRequirementsQuestList(missingLeaderQuests);
errorAlreadyPrinted = true;
}
if (missingPlayerAchievements.size())
{
ChatHandler(GetSession()).SendSysMessage(LANG_ACCESS_REQUIREMENT_COMPLETE_ACHIEVEMENTS);
PrettyPrintRequirementsAchievementsList(missingPlayerAchievements);
errorAlreadyPrinted = true;
}
if (missingLeaderAchievements.size())
{
ChatHandler(GetSession()).PSendSysMessage(LANG_ACCESS_REQUIREMENT_LEADER_COMPLETE_ACHIEVEMENTS, leaderName.c_str());
PrettyPrintRequirementsAchievementsList(missingLeaderAchievements);
errorAlreadyPrinted = true;
}
if (missingPlayerItems.size())
{
ChatHandler(GetSession()).SendSysMessage(LANG_ACCESS_REQUIREMENT_OBTAIN_ITEMS);
PrettyPrintRequirementsItemsList(missingPlayerItems);
errorAlreadyPrinted = true;
}
if (missingLeaderItems.size())
{
ChatHandler(GetSession()).PSendSysMessage(LANG_ACCESS_REQUIREMENT_LEADER_OBTAIN_ITEMS, leaderName.c_str());
PrettyPrintRequirementsItemsList(missingLeaderItems);
errorAlreadyPrinted = true;
}
if (ilvlRequirementNotMet)
{
ChatHandler(GetSession()).PSendSysMessage(LANG_ACCESS_REQUIREMENT_AVERAGE_ILVL_NOT_MET, ar->reqItemLevel, (uint16)GetAverageItemLevelForDF());
}
if (LevelMin)
{
GetSession()->SendAreaTriggerMessage(GetSession()->GetAcoreString(LANG_LEVEL_MINREQUIRED), LevelMin);
}
else if (LevelMax)
{
GetSession()->SendAreaTriggerMessage(GetSession()->GetAcoreString(LANG_ACCESS_REQUIREMENT_MAX_LEVEL), LevelMax);
}
else if (mapDiff->hasErrorMessage && !errorAlreadyPrinted)
{
SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty);
}
}
//Print the extra string
uint32 optionalStringID = sWorld->getIntConfig(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID);
if (optionalStringID > 0)
{
ChatHandler(GetSession()).SendSysMessage(optionalStringID);
}
}
return false;
}

View File

@@ -837,17 +837,23 @@ enum PlayerCharmedAISpells
#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE)
#define MAX_MONEY_AMOUNT (0x7FFFFFFF-1)
struct AccessRequirement
struct ProgressionRequirement
{
uint32 id;
TeamId faction;
std::string note;
uint32 priority;
bool checkLeaderOnly;
};
struct DungeonProgressionRequirements
{
uint8 levelMin;
uint8 levelMax;
uint32 item;
uint32 item2;
uint32 quest_A;
uint32 quest_H;
uint32 achievement;
std::string questFailedText;
uint16 reqItemLevel;
std::vector<ProgressionRequirement*> quests;
std::vector<ProgressionRequirement*> items;
std::vector<ProgressionRequirement*> achievements;
};
enum CharDeleteMethod
@@ -2449,7 +2455,10 @@ public:
[[nodiscard]] uint32 GetPendingBind() const { return _pendingBindId; }
void SendRaidInfo();
void SendSavedInstances();
bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false);
void PrettyPrintRequirementsQuestList(const std::vector<const ProgressionRequirement*>& missingQuests) const;
void PrettyPrintRequirementsAchievementsList(const std::vector<const ProgressionRequirement*>& missingAchievements) const;
void PrettyPrintRequirementsItemsList(const std::vector<const ProgressionRequirement*>& missingItems) const;
bool Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map, bool report = false);
bool CheckInstanceLoginValid();
[[nodiscard]] bool CheckInstanceCount(uint32 instanceId) const;

View File

@@ -338,8 +338,29 @@ ObjectMgr::~ObjectMgr()
for (DungeonEncounterList::iterator encounterItr = itr->second.begin(); encounterItr != itr->second.end(); ++encounterItr)
delete *encounterItr;
for (AccessRequirementContainer::iterator itr = _accessRequirementStore.begin(); itr != _accessRequirementStore.end(); ++itr)
delete itr->second;
for (DungeonProgressionRequirementsContainer::iterator itr = _accessRequirementStore.begin(); itr != _accessRequirementStore.end(); ++itr)
{
std::unordered_map<uint8, DungeonProgressionRequirements*> difficulties = itr->second;
for (auto difficultiesItr = difficulties.begin(); difficultiesItr != difficulties.end(); ++difficultiesItr)
{
for (auto questItr = difficultiesItr->second->quests.begin(); questItr != difficultiesItr->second->quests.end(); ++questItr)
{
delete* questItr;
}
for (auto achievementItr = difficultiesItr->second->achievements.begin(); achievementItr != difficultiesItr->second->achievements.end(); ++achievementItr)
{
delete* achievementItr;
}
for (auto itemsItr = difficultiesItr->second->items.begin(); itemsItr != difficultiesItr->second->items.end(); ++itemsItr)
{
delete* itemsItr;
}
delete difficultiesItr->second;
}
}
}
ObjectMgr* ObjectMgr::instance()
@@ -6171,96 +6192,164 @@ void ObjectMgr::LoadAccessRequirements()
if (!_accessRequirementStore.empty())
{
for (AccessRequirementContainer::iterator itr = _accessRequirementStore.begin(); itr != _accessRequirementStore.end(); ++itr)
delete itr->second;
for (DungeonProgressionRequirementsContainer::iterator itr = _accessRequirementStore.begin(); itr != _accessRequirementStore.end(); ++itr)
{
std::unordered_map<uint8, DungeonProgressionRequirements*> difficulties = itr->second;
for (auto difficultiesItr = difficulties.begin(); difficultiesItr != difficulties.end(); ++difficultiesItr)
{
for (auto questItr = difficultiesItr->second->quests.begin(); questItr != difficultiesItr->second->quests.end(); ++questItr)
{
delete* questItr;
}
for (auto achievementItr = difficultiesItr->second->achievements.begin(); achievementItr != difficultiesItr->second->achievements.end(); ++achievementItr)
{
delete* achievementItr;
}
for (auto itemsItr = difficultiesItr->second->items.begin(); itemsItr != difficultiesItr->second->items.end(); ++itemsItr)
{
delete* itemsItr;
}
delete difficultiesItr->second;
}
}
_accessRequirementStore.clear(); // need for reload case
}
// 0 1 2 3 4 5 6 7 8 9 10
QueryResult result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item, item2, quest_done_A, quest_done_H, completed_achievement, quest_failed_text, item_level FROM access_requirement");
if (!result)
// 0 1 2 3 4 5
QueryResult access_template_result = WorldDatabase.Query("SELECT id, map_id, difficulty, min_level, max_level, min_avg_item_level FROM dungeon_access_template");
if (!access_template_result)
{
sLog->outString(">> Loaded 0 access requirement definitions. DB table `access_requirement` is empty.");
sLog->outString(">> Loaded 0 access requirement definitions. DB table `dungeon_access_template` is empty.");
sLog->outString();
return;
}
uint32 count = 0;
uint32 countProgressionRequirements = 0;
do
{
Field* fields = result->Fetch();
Field* fields = access_template_result->Fetch();
++count;
//Get the common variables for the access requirements
uint8 dungeon_access_id = fields[0].GetUInt8();
uint32 mapid = fields[1].GetUInt32();
uint8 difficulty = fields[2].GetUInt8();
uint32 mapid = fields[0].GetUInt32();
uint8 difficulty = fields[1].GetUInt8();
uint32 requirement_ID = MAKE_PAIR32(mapid, difficulty);
//Set up the access requirements
DungeonProgressionRequirements* ar = new DungeonProgressionRequirements();
ar->levelMin = fields[3].GetUInt8();
ar->levelMax = fields[4].GetUInt8();
ar->reqItemLevel = fields[5].GetUInt16();
AccessRequirement* ar = new AccessRequirement();
ar->levelMin = fields[2].GetUInt8();
ar->levelMax = fields[3].GetUInt8();
ar->item = fields[4].GetUInt32();
ar->item2 = fields[5].GetUInt32();
ar->quest_A = fields[6].GetUInt32();
ar->quest_H = fields[7].GetUInt32();
ar->achievement = fields[8].GetUInt32();
ar->questFailedText = fields[9].GetString();
ar->reqItemLevel = fields[10].GetUInt16();
if (ar->item)
// 0 1 2 3 4 6
QueryResult progression_requirements_results = WorldDatabase.PQuery("SELECT requirement_type, requirement_id, requirement_note, faction, priority, leader_only FROM dungeon_access_requirements where dungeon_access_id = %u", dungeon_access_id);
if (progression_requirements_results)
{
ItemTemplate const* pProto = GetItemTemplate(ar->item);
if (!pProto)
do
{
sLog->outError("Key item %u does not exist for map %u difficulty %u, removing key requirement.", ar->item, mapid, difficulty);
ar->item = 0;
}
Field* progression_requirement_row = progression_requirements_results->Fetch();
const uint8 requirement_type = progression_requirement_row[0].GetUInt8();
const uint32 requirement_id = progression_requirement_row[1].GetUInt32();
const std::string requirement_note = progression_requirement_row[2].GetString();
const uint8 requirement_faction = progression_requirement_row[3].GetUInt8();
const uint8 requirement_priority = progression_requirement_row[4].IsNull() ? UINT8_MAX : progression_requirement_row[4].GetUInt8();
const bool requirement_checkLeaderOnly = progression_requirement_row[5].GetBool();
ProgressionRequirement* progression_requirement = new ProgressionRequirement();
progression_requirement->id = requirement_id;
progression_requirement->note = requirement_note;
progression_requirement->faction = (TeamId)requirement_faction;
progression_requirement->priority = requirement_priority;
progression_requirement->checkLeaderOnly = requirement_checkLeaderOnly;
std::vector<ProgressionRequirement*>* currentRequirementsList = nullptr;
switch (requirement_type)
{
case 0:
{
//Achievement
if (!sAchievementStore.LookupEntry(progression_requirement->id))
{
sLog->outErrorDb("Required achievement %u for faction %u does not exist for map %u difficulty %u, remove or fix this achievement requirement.", progression_requirement->id, requirement_faction, mapid, difficulty);
break;
}
currentRequirementsList = &ar->achievements;
break;
}
case 1:
{
//Quest
if (!GetQuestTemplate(progression_requirement->id))
{
sLog->outErrorDb("Required quest %u for faction %u does not exist for map %u difficulty %u, remove or fix this quest requirement.", progression_requirement->id, requirement_faction, mapid, difficulty);
break;
}
currentRequirementsList = &ar->quests;
break;
}
case 2:
{
//Item
ItemTemplate const* pProto = GetItemTemplate(progression_requirement->id);
if (!pProto)
{
sLog->outError("Required item %u for faction %u does not exist for map %u difficulty %u, remove or fix this item requirement.", progression_requirement->id, requirement_faction, mapid, difficulty);
break;
}
currentRequirementsList = &ar->items;
break;
}
default:
sLog->outError("requirement_type of %u is not valid for map %u difficulty %u. Please use 0 for achievements, 1 for quest, 2 for items or remove this entry from the db.", requirement_type, mapid, difficulty);
break;
}
//Check if array is valid and delete the progression requirement
if (!currentRequirementsList)
{
delete progression_requirement;
continue;
}
//Insert into the array
if (currentRequirementsList->size() > requirement_priority)
{
currentRequirementsList->insert(currentRequirementsList->begin() + requirement_priority, progression_requirement);
}
else
{
currentRequirementsList->push_back(progression_requirement);
}
} while (progression_requirements_results->NextRow());
}
if (ar->item2)
{
ItemTemplate const* pProto = GetItemTemplate(ar->item2);
if (!pProto)
{
sLog->outError("Second item %u does not exist for map %u difficulty %u, removing key requirement.", ar->item2, mapid, difficulty);
ar->item2 = 0;
}
}
//Sort all arrays for priority
auto sortFunction = [](const ProgressionRequirement* const a, const ProgressionRequirement* const b) {return a->priority > b->priority; };
std::sort(ar->achievements.begin(), ar->achievements.end(), sortFunction);
std::sort(ar->quests.begin(), ar->quests.end(), sortFunction);
std::sort(ar->items.begin(), ar->items.end(), sortFunction);
if (ar->quest_A)
{
if (!GetQuestTemplate(ar->quest_A))
{
sLog->outErrorDb("Required Alliance Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar->quest_A, mapid, difficulty);
ar->quest_A = 0;
}
}
countProgressionRequirements += ar->achievements.size();
countProgressionRequirements += ar->quests.size();
countProgressionRequirements += ar->items.size();
count++;
if (ar->quest_H)
{
if (!GetQuestTemplate(ar->quest_H))
{
sLog->outErrorDb("Required Horde Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar->quest_H, mapid, difficulty);
ar->quest_H = 0;
}
}
_accessRequirementStore[mapid][difficulty] = ar;
} while (access_template_result->NextRow());
if (ar->achievement)
{
if (!sAchievementStore.LookupEntry(ar->achievement))
{
sLog->outErrorDb("Required Achievement %u not exist for map %u difficulty %u, remove quest done requirement.", ar->achievement, mapid, difficulty);
ar->achievement = 0;
}
}
_accessRequirementStore[requirement_ID] = ar;
} while (result->NextRow());
sLog->outString(">> Loaded %u access requirement definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString(">> Loaded %u rows from dungeon_access_template and %u rows from dungeon_access_requirements in %u ms", count, countProgressionRequirements, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}

View File

@@ -31,7 +31,7 @@
#include <string>
class Item;
struct AccessRequirement;
struct DungeonProgressionRequirements;
struct PlayerClassInfo;
struct PlayerClassLevelInfo;
struct PlayerInfo;
@@ -704,7 +704,7 @@ public:
typedef std::unordered_map<uint32, uint32> AreaTriggerScriptContainer;
typedef std::unordered_map<uint32, AccessRequirement*> AccessRequirementContainer;
typedef std::unordered_map<uint32, std::unordered_map<uint8, DungeonProgressionRequirements*>> DungeonProgressionRequirementsContainer;
typedef std::unordered_map<uint32, RepRewardRate > RepRewardRateContainer;
typedef std::unordered_map<uint32, ReputationOnKillEntry> RepOnKillContainer;
@@ -832,11 +832,18 @@ public:
return nullptr;
}
[[nodiscard]] AccessRequirement const* GetAccessRequirement(uint32 mapid, Difficulty difficulty) const
[[nodiscard]] DungeonProgressionRequirements const* GetAccessRequirement(uint32 mapid, Difficulty difficulty) const
{
AccessRequirementContainer::const_iterator itr = _accessRequirementStore.find(MAKE_PAIR32(mapid, difficulty));
DungeonProgressionRequirementsContainer::const_iterator itr = _accessRequirementStore.find(mapid);
if (itr != _accessRequirementStore.end())
return itr->second;
{
std::unordered_map<uint8, DungeonProgressionRequirements*> difficultiesProgressionRequirements = itr->second;
auto difficultiesItr = difficultiesProgressionRequirements.find(difficulty);
if (difficultiesItr != difficultiesProgressionRequirements.end())
{
return difficultiesItr->second;
}
}
return nullptr;
}
@@ -1386,7 +1393,7 @@ private:
AreaTriggerContainer _areaTriggerStore;
AreaTriggerTeleportContainer _areaTriggerTeleportStore;
AreaTriggerScriptContainer _areaTriggerScriptStore;
AccessRequirementContainer _accessRequirementStore;
DungeonProgressionRequirementsContainer _accessRequirementStore;
DungeonEncounterContainer _dungeonEncounterStore;
RepRewardRateContainer _repRewardRateStore;

View File

@@ -844,6 +844,18 @@ enum AcoreStrings
LANG_RBAC_EMAIL_REQUIRED = 881,
// Room for in-game strings 882-999 not used
//Access Requirements
LANG_ACCESS_REQUIREMENT_COMPLETE_QUESTS = 882,
LANG_ACCESS_REQUIREMENT_COMPLETE_ACHIEVEMENTS = 883,
LANG_ACCESS_REQUIREMENT_OBTAIN_ITEMS = 884,
LANG_ACCESS_REQUIREMENT_NOTE = 885,
LANG_ACCESS_REQUIREMENT_NOT_MET = 886,
LANG_ACCESS_REQUIREMENT_AVERAGE_ILVL_NOT_MET = 887,
LANG_ACCESS_REQUIREMENT_MAX_LEVEL = 888,
LANG_ACCESS_REQUIREMENT_LEADER_COMPLETE_QUESTS = 889,
LANG_ACCESS_REQUIREMENT_LEADER_COMPLETE_ACHIEVEMENTS = 890,
LANG_ACCESS_REQUIREMENT_LEADER_OBTAIN_ITEMS = 891,
// Level 4 (CLI only commands)
LANG_COMMAND_EXIT = 1000,
LANG_ACCOUNT_DELETED = 1001,
@@ -1320,6 +1332,6 @@ enum AcoreStrings
LANG_BG_READY_CHECK_ERROR = 30084,
LANG_DEBUG_BG_CONF = 30085,
LANG_DEBUG_ARENA_CONF = 30086
LANG_DEBUG_ARENA_CONF = 30086,
};
#endif

View File

@@ -163,6 +163,8 @@ enum WorldBoolConfigs
CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE,
CONFIG_DEBUG_BATTLEGROUND,
CONFIG_DEBUG_ARENA,
CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL,
CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE,
CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID,
CONFIG_SET_SHAPASSHASH,
CONFIG_SET_BOP_ITEM_TRADEABLE,
@@ -364,6 +366,8 @@ enum WorldIntConfigs
CONFIG_CHARTER_COST_ARENA_5v5,
CONFIG_MAX_WHO_LIST_RETURN,
CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER,
CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE,
CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID,
CONFIG_GUILD_BANK_INITIAL_TABS,
CONFIG_GUILD_BANK_TAB_COST_0,
CONFIG_GUILD_BANK_TAB_COST_1,

View File

@@ -1403,6 +1403,10 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER] = sConfigMgr->GetOption<int32>("WaypointMovementStopTimeForPlayer", 120);
m_int_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE] = sConfigMgr->GetOption<int32>("DungeonAccessRequirements.PrintMode", 1);
m_bool_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL] = sConfigMgr->GetOption<bool>("DungeonAccessRequirements.PortalAvgIlevelCheck", false);
m_bool_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE] = sConfigMgr->GetOption<bool>("DungeonAccessRequirements.LFGLevelDBCOverride", false);
m_int_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID] = sConfigMgr->GetOption<int32>("DungeonAccessRequirements.OptionalStringID", 0);
m_int_configs[CONFIG_NPC_EVADE_IF_NOT_REACHABLE] = sConfigMgr->GetOption<int32>("NpcEvadeIfTargetIsUnreachable", 5);
m_int_configs[CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID] = sConfigMgr->GetOption<int32>("NpcRegenHPTimeIfTargetIsUnreachable", 10);
m_bool_configs[CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID] = sConfigMgr->GetOption<bool>("NpcRegenHPIfTargetIsUnreachable", true);

View File

@@ -55,7 +55,8 @@ public:
static std::vector<ChatCommand> reloadCommandTable =
{
{ "auctions", SEC_ADMINISTRATOR, true, &HandleReloadAuctionsCommand, "" },
{ "access_requirement", SEC_ADMINISTRATOR, true, &HandleReloadAccessRequirementCommand, "" },
{ "dungeon_access_template", SEC_ADMINISTRATOR, true, &HandleReloadDungeonAccessCommand, "" },
{ "dungeon_access_requirements", SEC_ADMINISTRATOR, true, &HandleReloadDungeonAccessCommand, "" },
{ "achievement_criteria_data", SEC_ADMINISTRATOR, true, &HandleReloadAchievementCriteriaDataCommand, "" },
{ "achievement_reward", SEC_ADMINISTRATOR, true, &HandleReloadAchievementRewardCommand, "" },
{ "all", SEC_ADMINISTRATOR, true, nullptr, "", reloadAllCommandTable },
@@ -175,7 +176,7 @@ public:
HandleReloadAllGossipsCommand(handler, "");
HandleReloadAllLocalesCommand(handler, "");
HandleReloadAccessRequirementCommand(handler, "");
HandleReloadDungeonAccessCommand(handler, "");
HandleReloadMailLevelRewardCommand(handler, "");
HandleReloadCommandCommand(handler, "");
HandleReloadReservedNameCommand(handler, "");
@@ -324,11 +325,11 @@ public:
return true;
}
static bool HandleReloadAccessRequirementCommand(ChatHandler* handler, const char* /*args*/)
static bool HandleReloadDungeonAccessCommand(ChatHandler* handler, const char* /*args*/)
{
sLog->outString("Re-Loading Access Requirement definitions...");
sLog->outString("Re-Loading Dungeon Access Requirement definitions...");
sObjectMgr->LoadAccessRequirements();
handler->SendGlobalGMSysMessage("DB table `access_requirement` reloaded.");
handler->SendGlobalGMSysMessage("DB tables `dungeon_access_template` AND `dungeon_access_requirements` reloaded.");
return true;
}

View File

@@ -3609,6 +3609,45 @@ Calculate.Gameoject.Zone.Area.Data = 0
LFG.Location.All = 0
#
# DungeonAccessRequirements.PrintMode
#
# Description: Select the preferred format to display information to the player who cannot enter a portal dungeon because when has not met the access requirements:
# Default: 1 - (Display only one requirement at a time (BlizzLike, like in the LFG interface))
# 0 - (Display no extra information, only "Requirements not met")
# 2 - (Display detailed requirements, all at once, with clickable links)
#
DungeonAccessRequirements.PrintMode = 1
#
# DungeonAccessRequirements.PortalAvgIlevelCheck
#
# Description: Enable average item level requirement when entering a dungeon/raid's portal (= deny the entry if player has too low average ilevel, like in LFG).
# Default: 0 - (Disabled -> Blizzlike)
# 1 - (Enabled)
DungeonAccessRequirements.PortalAvgIlevelCheck = 0
#
# DungeonAccessRequirements.LFGLevelDBCOverride
#
# Description: If enabled, use `min_level` and `max_level` values from table `dungeon_access_requirements` to list or to hide a dungeon from the LFG window.
# Default: 0 - (Disabled)
# 1 - (Enabled)
DungeonAccessRequirements.LFGLevelDBCOverride = 0
#
# DungeonAccessRequirements.OptionalStringID
#
# Description: Display an extra message from acore_strings in the chat after printing the dungeon access requirements.
# To enable it set the ID of your desired string from the table acore_strings
# Default: 0 - (Disabled)
# 1+ - (Enabled)
DungeonAccessRequirements.OptionalStringID = 0
#
# ICC Buff
# Description: Specify ICC buff