Files
mod-playerbots/src/strategy/values/GrindTargetValue.cpp
Yunfan Li 38912d4a8a Auto do quest feature (new rpg strategy) (#1034)
* New rpg startup speed up and refactor

* New rpg do quest

* Fix invalid height in quest poi

* Add quest accept and reward limitation

* New rpg quest improvement

* Organize quest log, reward quests and fix grind target

* Quest dropped statistic and remove redundant code

* Decrease grind relevance lower than loot

* Fix new rpg drop quest

* Go to reward quest instead of innkeeper when quest completed

* Fix incorrect logic in do quest reward

* Fix reset quests in factory

* Fix crash on grind target value

Co-authored-by: SaW <swerkhoven@outlook.com>

* Fix a minor error in DoCompletedQuest

* Let bots get rid of impossible quests faster

* Increase loot fluency (especially for caster)

* Remove seasonal quests from auto accept

* Enhance quest accept condition check

* Add questgiver check (limit acceptation of quest 7946)

* Questgiver check and localization

* Near npc fix

* Fix quest item report

* Add lowPriorityQuest set for quests can not be done

* Improve gameobjects loot

* Do complete quest

* FIx move far to teleport check

* Accept or reward quest from game objects

* Fix possible crash in rpg game objects

* Fix ChooseNpcOrGameObjectToInteract crash

---------

Co-authored-by: SaW <swerkhoven@outlook.com>
2025-03-14 21:31:33 +08:00

238 lines
7.5 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 "GrindTargetValue.h"
#include "NewRpgInfo.h"
#include "Playerbots.h"
#include "ReputationMgr.h"
#include "SharedDefines.h"
#include "ServerFacade.h"
Unit* GrindTargetValue::Calculate()
{
uint32 memberCount = 1;
Group* group = bot->GetGroup();
if (group)
memberCount = group->GetMembersCount();
Unit* target = nullptr;
uint32 assistCount = 0;
while (!target && assistCount < memberCount)
{
target = FindTargetForGrinding(assistCount++);
}
return target;
}
Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
{
uint32 memberCount = 1;
Group* group = bot->GetGroup();
Player* master = GetMaster();
if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() || !GET_PLAYERBOT_AI(master)))
master = nullptr;
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
for (ObjectGuid const guid : attackers)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
continue;
return unit;
}
GuidVector targets = *context->GetValue<GuidVector>("possible targets");
if (targets.empty())
return nullptr;
float distance = 0;
Unit* result = nullptr;
std::unordered_map<uint32, bool> needForQuestMap;
for (ObjectGuid const guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit)
continue;
auto& rep = bot->ToPlayer()->GetReputationMgr();
if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid &&
bot->GetReactionTo(unit) >= REP_NEUTRAL)
{
continue;
}
if (!bot->IsHostileTo(unit) && unit->GetNpcFlags() != UNIT_NPC_FLAG_NONE)
{
continue;
}
if (!bot->isHonorOrXPTarget(unit))
{
continue;
}
if (abs(bot->GetPositionZ() - unit->GetPositionZ()) > INTERACTION_DISTANCE)
continue;
if (!bot->InBattleground() && GetTargetingPlayerCount(unit) > assistCount)
continue;
// if (!bot->InBattleground() && master && master->GetDistance(unit) >= sPlayerbotAIConfig->grindDistance &&
// !sRandomPlayerbotMgr->IsRandomBot(bot)) continue;
// Bots in bot-groups no have a more limited range to look for grind target
if (!bot->InBattleground() && master && botAI->HasStrategy("follow", BotState::BOT_STATE_NON_COMBAT)
&& sServerFacade->GetDistance2d(master, unit) > sPlayerbotAIConfig->lootDistance)
{
if (botAI->HasStrategy("debug grind", BotState::BOT_STATE_NON_COMBAT))
botAI->TellMaster(chat->FormatWorldobject(unit) + " ignored (far from master).");
continue;
}
if (!bot->InBattleground() && (int)unit->GetLevel() - (int)bot->GetLevel() > 4 && !unit->GetGUID().IsPlayer())
continue;
if (Creature* creature = unit->ToCreature())
if (CreatureTemplate const* CreatureTemplate = creature->GetCreatureTemplate())
if (CreatureTemplate->rank > CREATURE_ELITE_NORMAL && !AI_VALUE(bool, "can fight elite"))
continue;
if (!bot->IsWithinLOSInMap(unit))
{
continue;
}
bool inactiveGrindStatus = botAI->rpgInfo.status == RPG_GO_GRIND ||
botAI->rpgInfo.status == RPG_NEAR_NPC ||
botAI->rpgInfo.status == RPG_REST ||
botAI->rpgInfo.status == RPG_GO_INNKEEPER ||
botAI->rpgInfo.status == RPG_DO_QUEST;
bool notHostile = !bot->IsHostileTo(unit); /*|| (unit->ToCreature() && unit->ToCreature()->IsCivilian());*/
float aggroRange = 30.0f;
if (unit->ToCreature())
aggroRange = std::min(30.0f, unit->ToCreature()->GetAggroRange(bot) + 10.0f);
bool outOfAggro = unit->ToCreature() && bot->GetDistance(unit) > aggroRange;
if (inactiveGrindStatus && (outOfAggro || notHostile))
{
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
if (!needForQuestMap[unit->GetEntry()])
continue;
}
if (group)
{
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
{
Player* member = ObjectAccessor::FindPlayer(itr->guid);
if (!member || !member->IsAlive())
continue;
float d = member->GetDistance(unit);
if (!result || d < distance)
{
distance = d;
result = unit;
}
}
}
else
{
float newdistance = bot->GetDistance(unit);
if (!result || (newdistance < distance))
{
distance = newdistance;
result = unit;
}
}
}
return result;
}
bool GrindTargetValue::needForQuest(Unit* target)
{
QuestStatusMap& questMap = bot->getQuestStatusMap();
for (auto& quest : questMap)
{
Quest const* questTemplate = sObjectMgr->GetQuestTemplate(quest.first);
if (!questTemplate)
continue;
uint32 questId = questTemplate->GetQuestId();
if (!questId)
continue;
QuestStatus status = bot->GetQuestStatus(questId);
if (status == QUEST_STATUS_INCOMPLETE)
{
const QuestStatusData* questStatus = &bot->getQuestStatusMap()[questId];
if (questTemplate->GetQuestLevel() > bot->GetLevel() + 5)
continue;
for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
{
int32 entry = questTemplate->RequiredNpcOrGo[j];
if (entry && entry > 0)
{
int required = questTemplate->RequiredNpcOrGoCount[j];
int available = questStatus->CreatureOrGOCount[j];
if (required && available < required && target->GetEntry() == entry)
return true;
}
}
if (CreatureTemplate const* data = sObjectMgr->GetCreatureTemplate(target->GetEntry()))
{
if (uint32 lootId = data->lootid)
{
if (LootTemplates_Creature.HaveQuestLootForPlayer(lootId, bot))
{
return true;
}
}
}
}
}
return false;
}
uint32 GrindTargetValue::GetTargetingPlayerCount(Unit* unit)
{
Group* group = bot->GetGroup();
if (!group)
return 0;
uint32 count = 0;
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
{
Player* member = ObjectAccessor::FindPlayer(itr->guid);
if (!member || !member->IsAlive() || member == bot)
continue;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(member);
if ((botAI && *botAI->GetAiObjectContext()->GetValue<Unit*>("current target") == unit) ||
(!botAI && member->GetTarget() == unit->GetGUID()))
++count;
}
return count;
}