mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 09:07:19 +00:00
460 lines
12 KiB
C++
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;
|
|
};
|