Files
mod-playerbots/src/strategy/values/QuestValues.cpp
2022-03-12 22:27:09 +01:00

460 lines
12 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "QuestValues.h"
#include "MapMgr.h"
#include "Playerbots.h"
#include "SharedValueContext.h"
//What kind of a relation does this entry have with this quest.
entryQuestRelationMap EntryQuestRelationMapValue::Calculate()
{
entryQuestRelationMap rMap;
for (auto relation : *sObjectMgr->GetCreatureQuestRelationMap())
rMap[relation.first][relation.second] |= (int)QuestRelationFlag::questGiver;
for (auto relation : *sObjectMgr->GetCreatureQuestInvolvedRelationMap())
rMap[relation.first][relation.second] |= (int)QuestRelationFlag::questTaker;
for (auto relation : *sObjectMgr->GetGOQuestRelationMap())
rMap[-(int32)relation.first][relation.second] |= (int)QuestRelationFlag::questGiver;
for (auto relation : *sObjectMgr->GetGOQuestInvolvedRelationMap())
rMap[-(int32)relation.first][relation.second] |= (int)QuestRelationFlag::questGiver;
//Quest objectives
ObjectMgr::QuestMap const& questMap = sObjectMgr->GetQuestTemplates();
for (auto& questItr : questMap)
{
uint32 questId = questItr.first;
Quest* quest = questItr.second;
for (uint32 objective = 0; objective < QUEST_OBJECTIVES_COUNT; objective++)
{
uint32 relationFlag = 1 << objective;
//Kill objective
if (quest->RequiredNpcOrGo[objective])
rMap[quest->RequiredNpcOrGo[objective]][questId] |= relationFlag;
//Loot objective
if (quest->RequiredItemId[objective])
{
for (auto& entry : GAI_VALUE2(std::vector<int32>, "item drop list", quest->RequiredItemId[objective]))
rMap[entry][questId] |= relationFlag;
}
}
}
return rMap;
}
//Get all the objective entries for a specific quest.
void FindQuestObjectData::GetObjectiveEntries()
{
relationMap = GAI_VALUE(entryQuestRelationMap, "entry quest relation");
}
//Data worker. Checks for a specific creature what quest they are needed for and puts them in the proper place in the quest map.
void FindQuestObjectData::operator()(CreatureData const& creData)
{
uint32 entry = creData.id1;
for (auto& relation : relationMap[entry])
{
uint32 questId = relation.first;
uint32 flag = relation.second;
data[questId][flag][entry].push_back(GuidPosition(creData));
}
}
//GameObject data worker. Checks for a specific gameObject what quest they are needed for and puts them in the proper place in the quest map.
void FindQuestObjectData::operator()(GameObjectData const& goData)
{
int32 entry = goData.id * -1;
for (auto& relation : relationMap[entry])
{
uint32 questId = relation.first;
uint32 flag = relation.second;
data[questId][flag][entry].push_back(GuidPosition(goData));
}
}
//Goes past all creatures and gameobjects and creatures the full quest guid map.
questGuidpMap QuestGuidpMapValue::Calculate()
{
FindQuestObjectData worker;
for (auto const& itr : sObjectMgr->GetAllCreatureData())
worker(itr.second);
for (auto const& itr : sObjectMgr->GetAllGOData())
worker(itr.second);
return worker.GetResult();
}
//Selects all questgivers for a specific level (range).
questGiverMap QuestGiversValue::Calculate()
{
uint32 level = 0;
std::string const q = getQualifier();
bool hasQualifier = !q.empty();
if (hasQualifier)
level = stoi(q);
questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map");
questGiverMap guidps;
for (auto& qPair : questMap)
{
for (auto& entry : qPair.second[(int)QuestRelationFlag::questGiver])
{
for (auto& guidp : entry.second)
{
uint32 questId = qPair.first;
if (hasQualifier)
{
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (quest && (level < quest->GetMinLevel() || (int)level > quest->GetQuestLevel() + 10))
continue;
}
guidps[questId].push_back(guidp);
}
}
}
return guidps;
}
std::vector<GuidPosition> ActiveQuestGiversValue::Calculate()
{
questGiverMap qGivers = GAI_VALUE2(questGiverMap, "quest givers", bot->getLevel());
std::vector<GuidPosition> retQuestGivers;
for (auto& qGiver : qGivers)
{
uint32 questId = qGiver.first;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
{
continue;
}
if (!bot->CanTakeQuest(quest, false))
continue;
QuestStatus status = bot->GetQuestStatus(questId);
if (status != QUEST_STATUS_NONE)
continue;
for (auto& guidp : qGiver.second)
{
CreatureTemplate const* creatureTemplate = guidp.GetCreatureTemplate();
if (creatureTemplate)
{
if (bot->GetFactionReactionTo(bot->GetFactionTemplateEntry(), sFactionTemplateStore.LookupEntry(creatureTemplate->faction)) < REP_FRIENDLY)
continue;
}
if (guidp.isDead())
continue;
retQuestGivers.push_back(guidp);
}
}
return retQuestGivers;
}
std::vector<GuidPosition> ActiveQuestTakersValue::Calculate()
{
questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map");
std::vector<GuidPosition> retQuestTakers;
QuestStatusMap& questStatusMap = bot->getQuestStatusMap();
for (auto& questStatus : questStatusMap)
{
uint32 questId = questStatus.first;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
{
continue;
}
QuestStatus status = questStatus.second.Status;
if ((status != QUEST_STATUS_COMPLETE || bot->GetQuestRewardStatus(questId)) && (!quest->IsAutoComplete() || !bot->CanTakeQuest(quest, false)))
continue;
auto q = questMap.find(questId);
if (q == questMap.end())
continue;
auto qt = q->second.find((int) QuestRelationFlag::questTaker);
if (qt == q->second.end())
continue;
for (auto& entry : qt->second)
{
if (entry.first > 0)
{
if (CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry.first))
{
if (bot->GetFactionReactionTo(bot->GetFactionTemplateEntry(), sFactionTemplateStore.LookupEntry(info->faction)) < REP_FRIENDLY)
continue;
}
}
for (auto& guidp : entry.second)
{
if (guidp.isDead())
continue;
retQuestTakers.push_back(guidp);
}
}
}
return retQuestTakers;
}
std::vector<GuidPosition> ActiveQuestObjectivesValue::Calculate()
{
questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map");
std::vector<GuidPosition> retQuestObjectives;
QuestStatusMap& questStatusMap = bot->getQuestStatusMap();
for (auto& questStatus : questStatusMap)
{
uint32 questId = questStatus.first;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
{
continue;
}
QuestStatusData statusData = questStatus.second;
if (statusData.Status != QUEST_STATUS_INCOMPLETE)
continue;
for (uint32 objective = 0; objective < QUEST_OBJECTIVES_COUNT; objective++)
{
if (quest->RequiredItemCount[objective])
{
uint32 reqCount = quest->RequiredItemCount[objective];
uint32 hasCount = statusData.ItemCount[objective];
if (!reqCount || hasCount >= reqCount)
continue;
}
if (quest->RequiredNpcOrGoCount[objective])
{
uint32 reqCount = quest->RequiredItemCount[objective];
uint32 hasCount = statusData.CreatureOrGOCount[objective];
if (!reqCount || hasCount >= reqCount)
continue;
}
auto q = questMap.find(questId);
if (q == questMap.end())
continue;
auto qt = q->second.find((int) QuestRelationFlag(1 << objective));
if (qt == q->second.end())
continue;
for (auto& entry : qt->second)
{
for (auto& guidp : entry.second)
{
if (guidp.isDead())
continue;
retQuestObjectives.push_back(guidp);
}
}
}
}
return retQuestObjectives;
}
uint8 FreeQuestLogSlotValue::Calculate()
{
uint8 numQuest = 0;
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 questId = bot->GetQuestSlotQuestId(slot);
if (!questId)
continue;
Quest const* quest = sObjectMgr->GetQuestTemplate(questId);
if (!quest)
continue;
numQuest++;
}
return MAX_QUEST_LOG_SIZE - numQuest;
}
uint32 DialogStatusValue::getDialogStatus(Player* bot, int32 questgiver, uint32 questId)
{
uint32 dialogStatus = DIALOG_STATUS_NONE;
QuestRelationBounds rbounds; // QuestRelations (quest-giver)
QuestRelationBounds irbounds; // InvolvedRelations (quest-finisher)
if (questgiver > 0)
{
rbounds = sObjectMgr->GetCreatureQuestRelationBounds(questgiver);
irbounds = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(questgiver);
}
else
{
rbounds = sObjectMgr->GetGOQuestRelationBounds(questgiver * -1);
irbounds = sObjectMgr->GetGOQuestInvolvedRelationBounds(questgiver * -1);
}
// Check markings for quest-finisher
for (QuestRelations::const_iterator itr = irbounds.first; itr != irbounds.second; ++itr)
{
if (questId && itr->second != questId)
continue;
Quest const* pQuest = sObjectMgr->GetQuestTemplate(itr->second);
if (!pQuest)
{
continue;
}
uint32 dialogStatusNew = DIALOG_STATUS_NONE;
QuestStatus status = bot->GetQuestStatus(itr->second);
if ((status == QUEST_STATUS_COMPLETE && !bot->GetQuestRewardStatus(itr->second)) ||
(pQuest->IsAutoComplete() && bot->CanTakeQuest(pQuest, false)))
{
if (pQuest->IsAutoComplete() && pQuest->IsRepeatable())
{
dialogStatusNew = DIALOG_STATUS_REWARD_REP;
}
else
{
dialogStatusNew = DIALOG_STATUS_REWARD2;
}
}
else if (status == QUEST_STATUS_INCOMPLETE)
{
dialogStatusNew = DIALOG_STATUS_INCOMPLETE;
}
if (dialogStatusNew > dialogStatus)
{
dialogStatus = dialogStatusNew;
}
}
// check markings for quest-giver
for (QuestRelations::const_iterator itr = rbounds.first; itr != rbounds.second; ++itr)
{
if (questId && itr->second != questId)
continue;
Quest const* pQuest = sObjectMgr->GetQuestTemplate(itr->second);
if (!pQuest)
{
continue;
}
uint32 dialogStatusNew = DIALOG_STATUS_NONE;
QuestStatus status = bot->GetQuestStatus(itr->second);
if (status == QUEST_STATUS_NONE) // For all other cases the mark is handled either at some place else, or with involved-relations already
{
if (bot->CanSeeStartQuest(pQuest))
{
if (bot->SatisfyQuestLevel(pQuest, false))
{
int32 lowLevelDiff = sWorld->getIntConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF);
if (pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && bot->getQuestStatusMap()[itr->second].Status == QUEST_STATUS_REWARDED))
{
dialogStatusNew = DIALOG_STATUS_REWARD_REP;
}
else if (lowLevelDiff < 0 || bot->getLevel() <= bot->GetQuestLevel(pQuest) + uint32(lowLevelDiff))
{
dialogStatusNew = DIALOG_STATUS_AVAILABLE;
}
else
{
dialogStatusNew = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
}
}
else
{
dialogStatusNew = DIALOG_STATUS_UNAVAILABLE;
}
}
}
if (dialogStatusNew > dialogStatus)
{
dialogStatus = dialogStatusNew;
}
}
return dialogStatus;
}
uint32 DialogStatusValue::Calculate()
{
return getDialogStatus(bot, stoi(getQualifier()));
}
uint32 DialogStatusQuestValue::Calculate()
{
return getDialogStatus(bot, getMultiQualifier(getQualifier(), 0), getMultiQualifier(getQualifier(), 1));
}
bool CanAcceptQuestValue::Calculate()
{
return AI_VALUE2(uint32, "dialog status", getQualifier()) == DIALOG_STATUS_AVAILABLE;
};
bool CanAcceptQuestLowLevelValue::Calculate()
{
uint32 dialogStatus = AI_VALUE2(uint32, "dialog status", getQualifier());
return dialogStatus == DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
};
bool CanTurnInQuestValue::Calculate()
{
uint32 dialogStatus = AI_VALUE2(uint32, "dialog status", getQualifier());
return dialogStatus == DIALOG_STATUS_REWARD2 || dialogStatus == DIALOG_STATUS_REWARD || dialogStatus == DIALOG_STATUS_REWARD_REP;
};