Files
mod-playerbots/src/Ai/Base/Value/EnemyPlayerValue.cpp
bashermens 13fff46fa0 Improper singletons migration to clean Meyer's singletons (cherry-pick) (#2082)
# Pull Request

- Applies the clean and corrected singletons, Meyer pattern. (cherry
picked from @SmashingQuasar )

Testing by just playing the game in various ways. Been tested by myself
@Celandriel and @SmashingQuasar
---

## Complexity & Impact

- Does this change add new decision branches?
    - [x] No
    - [ ] Yes (**explain below**)

- Does this change increase per-bot or per-tick processing?
    - [x] No
    - [ ] Yes (**describe and justify impact**)

- Could this logic scale poorly under load?
    - [x] No
    - [ ] Yes (**explain why**)

---

## Defaults & Configuration

- Does this change modify default bot behavior?
    - [x] No
    - [ ] Yes (**explain why**)

---

## AI Assistance

- Was AI assistance (e.g. ChatGPT or similar tools) used while working
on this change?
    - [x] No
    - [ ] Yes (**explain below**)
---

## Final Checklist

- [x] Stability is not compromised
- [x] Performance impact is understood, tested, and acceptable
- [x] Added logic complexity is justified and explained
- [x] Documentation updated if needed

---

## Notes for Reviewers

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.

---------

Co-authored-by: Nicolas Lebacq <nicolas.cordier@outlook.com>
Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
2026-01-30 21:49:37 +01:00

169 lines
5.4 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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 "EnemyPlayerValue.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "Vehicle.h"
bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit)
{
bool inCannon = botAI->IsInVehicle(false, true);
Player* enemy = dynamic_cast<Player*>(unit);
if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() &&
!sPlayerbotAIConfig.IsPvpProhibited(enemy->GetZoneId(), enemy->GetAreaId()) &&
!enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) &&
((inCannon || !enemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))) &&
/*!enemy->HasStealthAura() && !enemy->HasInvisibilityAura()*/ enemy->CanSeeOrDetect(bot) &&
!(enemy->HasSpiritOfRedemptionAura()))
return true;
return false;
}
Unit* EnemyPlayerValue::Calculate()
{
bool controllingCannon = false;
bool controllingVehicle = false;
if (Vehicle* vehicle = bot->GetVehicle())
{
VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot);
if (!seat || !seat->CanControl()) // not in control of vehicle so cant attack anyone
return nullptr;
VehicleEntry const* vi = vehicle->GetVehicleInfo();
if (vi && vi->m_flags & VEHICLE_FLAG_FIXED_POSITION)
controllingCannon = true;
else
controllingVehicle = true;
}
// 1. Check units we are currently in combat with.
std::vector<Unit*> targets;
Unit* pVictim = bot->GetVictim();
HostileReference* pReference = bot->getHostileRefMgr().getFirst();
while (pReference)
{
ThreatMgr* threatMgr = pReference->GetSource();
if (Unit* pTarget = threatMgr->GetOwner())
{
if (pTarget != pVictim && pTarget->IsPlayer() && pTarget->CanSeeOrDetect(bot) &&
bot->IsWithinDist(pTarget, VISIBILITY_DISTANCE_NORMAL))
{
if (bot->GetTeamId() == TEAM_HORDE)
{
if (pTarget->HasAura(23333))
return pTarget;
}
else
{
if (pTarget->HasAura(23335))
return pTarget;
}
targets.push_back(pTarget);
}
}
pReference = pReference->next();
}
if (!targets.empty())
{
std::sort(targets.begin(), targets.end(),
[&](Unit const* pUnit1, Unit const* pUnit2)
{ return bot->GetDistance(pUnit1) < bot->GetDistance(pUnit2); });
return *targets.begin();
}
// 2. Find enemy player in range.
GuidVector players = AI_VALUE(GuidVector, "nearest enemy players");
float const maxAggroDistance = GetMaxAttackDistance();
for (auto const& gTarget : players)
{
Unit* pUnit = botAI->GetUnit(gTarget);
if (!pUnit)
continue;
Player* pTarget = dynamic_cast<Player*>(pUnit);
if (!pTarget)
continue;
if (pTarget == pVictim)
continue;
if (bot->GetTeamId() == TEAM_HORDE)
{
if (pTarget->HasAura(23333))
return pTarget;
}
else
{
if (pTarget->HasAura(23335))
return pTarget;
}
// Aggro weak enemies from further away.
// If controlling mobile vehicle only agro close enemies (otherwise will never reach objective)
uint32 const aggroDistance = controllingVehicle ? 5.0f
: (controllingCannon || bot->GetHealth() > pTarget->GetHealth()) ? maxAggroDistance
: 20.0f;
if (!bot->IsWithinDist(pTarget, aggroDistance))
continue;
if (bot->IsWithinLOSInMap(pTarget) &&
(controllingCannon || (fabs(bot->GetPositionZ() - pTarget->GetPositionZ()) < 30.0f)))
return pTarget;
}
// 3. Check party attackers.
if (Group* pGroup = bot->GetGroup())
{
for (GroupReference* itr = pGroup->GetFirstMember(); itr != nullptr; itr = itr->next())
{
if (Unit* pMember = itr->GetSource())
{
if (pMember == bot)
continue;
if (ServerFacade::instance().GetDistance2d(bot, pMember) > 30.0f)
continue;
if (Unit* pAttacker = pMember->getAttackerForHelper())
if (pAttacker->IsPlayer() && bot->IsWithinDist(pAttacker, maxAggroDistance * 2.0f) &&
bot->IsWithinLOSInMap(pAttacker) && pAttacker != pVictim && pAttacker->CanSeeOrDetect(bot))
return pAttacker;
}
}
}
return nullptr;
}
float EnemyPlayerValue::GetMaxAttackDistance()
{
if (!bot->GetBattleground())
return 60.0f;
Battleground* bg = bot->GetBattleground();
if (!bg)
return 40.0f;
BattlegroundTypeId bgType = bg->GetBgTypeID();
if (bgType == BATTLEGROUND_RB)
bgType = bg->GetBgTypeID(true);
if (bgType == BATTLEGROUND_IC)
{
if (botAI->IsInVehicle(false, true))
return 120.0f;
}
return 40.0f;
}