/* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. */ #include "AttackersValue.h" #include "CellImpl.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "Playerbots.h" #include "ReputationMgr.h" #include "ServerFacade.h" GuidVector AttackersValue::Calculate() { std::unordered_set targets; GuidVector result; if (!botAI->AllowActivity(ALL_ACTIVITY)) return result; AddAttackersOf(bot, targets); if (Group* group = bot->GetGroup()) AddAttackersOf(group, targets); RemoveNonThreating(targets); // prioritized target GuidVector prioritizedTargets = AI_VALUE(GuidVector, "prioritized targets"); for (ObjectGuid target : prioritizedTargets) { Unit* unit = botAI->GetUnit(target); if (unit && IsValidTarget(unit, bot)) targets.insert(unit); } if (Group* group = bot->GetGroup()) { ObjectGuid skullGuid = group->GetTargetIcon(7); Unit* skullTarget = botAI->GetUnit(skullGuid); if (skullTarget && IsValidTarget(skullTarget, bot)) targets.insert(skullTarget); } for (Unit* unit : targets) result.push_back(unit->GetGUID()); if (bot->duel && bot->duel->Opponent) result.push_back(bot->duel->Opponent->GetGUID()); // workaround for bots of same faction not fighting in arena if (bot->InArena()) { GuidVector possibleTargets = AI_VALUE(GuidVector, "possible targets"); for (ObjectGuid const guid : possibleTargets) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsPlayer() && IsValidTarget(unit, bot)) result.push_back(unit->GetGUID()); } } return result; } void AttackersValue::AddAttackersOf(Group* group, std::unordered_set& targets) { 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 || member->GetMapId() != bot->GetMapId() || sServerFacade->GetDistance2d(bot, member) > sPlayerbotAIConfig->sightDistance) continue; AddAttackersOf(member, targets); } } struct AddGuardiansHelper { explicit AddGuardiansHelper(std::vector& units) : units(units) {} void operator()(Unit* target) const { units.push_back(target); } std::vector& units; }; void AttackersValue::AddAttackersOf(Player* player, std::unordered_set& targets) { if (!player || !player->IsInWorld() || player->IsBeingTeleported()) return; HostileRefMgr& refManager = player->getHostileRefMgr(); HostileReference* ref = refManager.getFirst(); if (!ref) return; while (ref) { ThreatMgr* threatMgr = ref->GetSource(); Unit* attacker = threatMgr->GetOwner(); if (player->IsValidAttackTarget(attacker) && player->GetDistance2d(attacker) < sPlayerbotAIConfig->sightDistance) targets.insert(attacker); ref = ref->next(); } } void AttackersValue::RemoveNonThreating(std::unordered_set& targets) { for (std::unordered_set::iterator tIter = targets.begin(); tIter != targets.end();) { Unit* unit = *tIter; if (bot->GetMapId() != unit->GetMapId() || !hasRealThreat(unit) || !IsValidTarget(unit, bot)) { std::unordered_set::iterator tIter2 = tIter; ++tIter; targets.erase(tIter2); } else ++tIter; } } bool AttackersValue::hasRealThreat(Unit* attacker) { return attacker && attacker->IsInWorld() && attacker->IsAlive() && !attacker->IsPolymorphed() && // !attacker->isInRoots() && !attacker->IsFriendlyTo(bot); (attacker->GetThreatMgr().getCurrentVictim() || dynamic_cast(attacker)); } bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float /*range*/) { PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (!botAI) return false; // Basic check if (!attacker) return false; // bool inCannon = botAI->IsInVehicle(false, true); // bool enemy = botAI->GetAiObjectContext()->GetValue("enemy player target")->Get(); // Validity checks if (!attacker->IsVisible() || !attacker->IsInWorld() || attacker->GetMapId() != bot->GetMapId()) return false; if (attacker->isDead() || attacker->HasSpiritOfRedemptionAura()) return false; // Flag checks if (attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2)) return false; if (attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) || attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) return false; // Relationship checks if (attacker->IsFriendlyTo(bot)) return false; // Critter exception if (attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) return false; // Visibility check if (!bot->CanSeeOrDetect(attacker)) return false; // PvP prohibition checks (skip for duels) if ((attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet()) && (!bot->duel || bot->duel->Opponent != attacker) && (sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) || sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()))) { // This will stop aggresive pets from starting an attack. // This will stop currently attacking pets from continuing their attack. // This will first require the bot to change from a combat strat. It will // not be reached if the bot only switches targets, including NPC targets. for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin(); itr != bot->m_Controlled.end(); ++itr) { Creature* creature = dynamic_cast(*itr); if (creature && creature->GetVictim() == attacker) { creature->AttackStop(); if (CharmInfo* charmInfo = creature->GetCharmInfo()) charmInfo->SetIsCommandAttack(false); } } return false; } // Unflagged player check if (attacker->IsPlayer() && !attacker->IsPvP() && !attacker->IsFFAPvP() && (!bot->duel || bot->duel->Opponent != attacker)) return false; // Creature-specific checks Creature* c = attacker->ToCreature(); if (c) { if (c->IsInEvadeMode()) return false; bool leaderHasThreat = false; if (bot->GetGroup() && botAI->GetMaster()) leaderHasThreat = attacker->GetThreatMgr().GetThreat(botAI->GetMaster()); bool isMemberBotGroup = false; if (bot->GetGroup() && botAI->GetMaster()) { PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(botAI->GetMaster()); if (masterBotAI && !masterBotAI->IsRealPlayer()) isMemberBotGroup = true; } bool canAttack = (!isMemberBotGroup && botAI->HasStrategy("attack tagged", BOT_STATE_NON_COMBAT)) || leaderHasThreat || (!c->hasLootRecipient() && (!c->GetVictim() || (c->GetVictim() && ((!c->GetVictim()->IsPlayer() || bot->IsInSameGroupWith(c->GetVictim()->ToPlayer())) || (botAI->GetMaster() && c->GetVictim() == botAI->GetMaster()))))) || c->isTappedBy(bot); if (!canAttack) return false; } return true; } bool AttackersValue::IsValidTarget(Unit* attacker, Player* bot) { return IsPossibleTarget(attacker, bot) && bot->IsWithinLOSInMap(attacker); // (attacker->GetThreatMgr().getCurrentVictim() || attacker->GetGuidValue(UNIT_FIELD_TARGET) || // attacker->GetGUID().IsPlayer() || attacker->GetGUID() == // GET_PLAYERBOT_AI(bot)->GetAiObjectContext()->GetValue("pull target")->Get()); } bool PossibleAddsValue::Calculate() { GuidVector possible = botAI->GetAiObjectContext()->GetValue("possible targets no los")->Get(); GuidVector attackers = botAI->GetAiObjectContext()->GetValue("attackers")->Get(); for (ObjectGuid const guid : possible) { if (find(attackers.begin(), attackers.end(), guid) != attackers.end()) continue; if (Unit* add = botAI->GetUnit(guid)) { if (!add->GetTarget() && !add->GetThreatMgr().getCurrentVictim() && add->IsHostileTo(bot)) { for (ObjectGuid const attackerGUID : attackers) { Unit* attacker = botAI->GetUnit(attackerGUID); if (!attacker) continue; float dist = sServerFacade->GetDistance2d(attacker, add); if (sServerFacade->IsDistanceLessOrEqualThan(dist, sPlayerbotAIConfig->aoeRadius * 1.5f)) continue; if (sServerFacade->IsDistanceLessOrEqualThan(dist, sPlayerbotAIConfig->aggroDistance)) return true; } } } } return false; }