/* * Copyright (C) 2016+ AzerothCore , 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, "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 ActiveQuestGiversValue::Calculate() { questGiverMap qGivers = GAI_VALUE2(questGiverMap, "quest givers", bot->getLevel()); std::vector 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 ActiveQuestTakersValue::Calculate() { questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map"); std::vector 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 ActiveQuestObjectivesValue::Calculate() { questGuidpMap questMap = GAI_VALUE(questGuidpMap, "quest guidp map"); std::vector 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; };