Files
mod-playerbots/src/Ai/Base/Value/AoeValues.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

161 lines
5.0 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 "AoeValues.h"
#include "Playerbots.h"
#include "ServerFacade.h"
#include "SpellAuraEffects.h"
GuidVector FindMaxDensity(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets");
std::map<ObjectGuid, GuidVector> groups;
uint32 maxCount = 0;
ObjectGuid maxGroup;
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
for (GuidVector::iterator j = units.begin(); j != units.end(); ++j)
{
Unit* other = botAI->GetUnit(*j);
if (!other)
continue;
float d = ServerFacade::instance().GetDistance2d(unit, other);
if (ServerFacade::instance().IsDistanceLessOrEqualThan(d, sPlayerbotAIConfig.aoeRadius * 2))
groups[*i].push_back(*j);
}
if (maxCount < groups[*i].size())
{
maxCount = groups[*i].size();
maxGroup = *i;
}
}
if (!maxCount)
return GuidVector();
return groups[maxGroup];
}
WorldLocation AoePositionValue::Calculate()
{
GuidVector group = FindMaxDensity(bot);
if (group.empty())
return WorldLocation();
// Note: don't know where these values come from or even used.
float x1 = 0.f;
float y1 = 0.f;
float x2 = 0.f;
float y2 = 0.f;
for (GuidVector::iterator i = group.begin(); i != group.end(); ++i)
{
Unit* unit = GET_PLAYERBOT_AI(bot)->GetUnit(*i);
if (!unit)
continue;
if (i == group.begin() || x1 > unit->GetPositionX())
x1 = unit->GetPositionX();
if (i == group.begin() || x2 < unit->GetPositionX())
x2 = unit->GetPositionX();
if (i == group.begin() || y1 > unit->GetPositionY())
y1 = unit->GetPositionY();
if (i == group.begin() || y2 < unit->GetPositionY())
y2 = unit->GetPositionY();
}
float x = (x1 + x2) / 2;
float y = (y1 + y2) / 2;
float z = bot->GetPositionZ() + CONTACT_DISTANCE;
;
bot->UpdateAllowedPositionZ(x, y, z);
return WorldLocation(bot->GetMapId(), x, y, z, 0);
}
uint8 AoeCountValue::Calculate() { return FindMaxDensity(bot).size(); }
bool HasAreaDebuffValue::Calculate()
{
for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; auraType++)
{
Unit::AuraEffectList const& auras = botAI->GetBot()->GetAuraEffectsByType((AuraType)auraType);
if (auras.empty())
continue;
for (AuraEffect const* aurEff : auras)
{
SpellInfo const* proto = aurEff->GetSpellInfo();
if (!proto)
continue;
uint32 trigger_spell_id = proto->Effects[aurEff->GetEffIndex()].TriggerSpell;
if (trigger_spell_id == 29767) // Overload
{
return true;
}
else
{
return (!proto->IsPositive() && aurEff->IsPeriodic() && proto->HasAreaAuraEffect());
}
}
}
return false;
}
Aura* AreaDebuffValue::Calculate()
{
// Unit::AuraApplicationMap& map = bot->GetAppliedAuras();
Unit::AuraEffectList const& aurasPeriodicDamage = bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE);
Unit::AuraEffectList const& aurasPeriodicDamagePercent =
bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE_PERCENT);
Unit::AuraEffectList const& aurasPeriodicTriggerSpell =
bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL);
Unit::AuraEffectList const& aurasPeriodicTriggerWithValueSpell =
bot->GetAuraEffectsByType(SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE);
Unit::AuraEffectList const& aurasDummy = bot->GetAuraEffectsByType(SPELL_AURA_DUMMY);
for (const Unit::AuraEffectList& list : {aurasPeriodicDamage, aurasPeriodicDamagePercent, aurasPeriodicTriggerSpell,
aurasPeriodicTriggerWithValueSpell, aurasDummy})
{
for (auto i = list.begin(); i != list.end(); ++i)
{
AuraEffect* aurEff = *i;
if (!aurEff)
continue;
Aura* aura = aurEff->GetBase();
if (!aura)
continue;
AuraObjectType type = aura->GetType();
bool isPositive = aura->GetSpellInfo()->IsPositive();
if (type == DYNOBJ_AURA_TYPE && !isPositive)
{
DynamicObject* dynOwner = aura->GetDynobjOwner();
if (!dynOwner)
{
continue;
}
// float radius = dynOwner->GetRadius();
// if (radius > 12.0f)
// continue;
return aura;
}
}
}
return nullptr;
}