mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-24 05:56:23 +00:00
[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038)
This commit is contained in:
25
src/Ai/Base/Value/ActiveSpellValue.cpp
Normal file
25
src/Ai/Base/Value/ActiveSpellValue.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 "ActiveSpellValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
uint32 ActiveSpellValue::Calculate()
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
for (uint8 type = CURRENT_MELEE_SPELL; type <= CURRENT_CHANNELED_SPELL; ++type)
|
||||
{
|
||||
if (Spell* spell = bot->GetCurrentSpell((CurrentSpellTypes)type))
|
||||
{
|
||||
if (spell->m_spellInfo)
|
||||
{
|
||||
return spell->m_spellInfo->Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
23
src/Ai/Base/Value/ActiveSpellValue.h
Normal file
23
src/Ai/Base/Value/ActiveSpellValue.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ACTIVESPELLVALUE_H
|
||||
#define _PLAYERBOT_ACTIVESPELLVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class ActiveSpellValue : public CalculatedValue<uint32>
|
||||
{
|
||||
public:
|
||||
ActiveSpellValue(PlayerbotAI* botAI, std::string const name = "active spell") : CalculatedValue<uint32>(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
37
src/Ai/Base/Value/AlwaysLootListValue.cpp
Normal file
37
src/Ai/Base/Value/AlwaysLootListValue.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "AlwaysLootListValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::string const AlwaysLootListValue::Save()
|
||||
{
|
||||
std::ostringstream out;
|
||||
bool first = true;
|
||||
for (std::set<uint32>::iterator i = value.begin(); i != value.end(); ++i)
|
||||
{
|
||||
if (!first)
|
||||
out << ",";
|
||||
else
|
||||
first = false;
|
||||
|
||||
out << *i;
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
bool AlwaysLootListValue::Load(std::string const text)
|
||||
{
|
||||
value.clear();
|
||||
|
||||
std::vector<std::string> ss = split(text, ',');
|
||||
for (std::vector<std::string>::iterator i = ss.begin(); i != ss.end(); ++i)
|
||||
{
|
||||
value.insert(atoi(i->c_str()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
28
src/Ai/Base/Value/AlwaysLootListValue.h
Normal file
28
src/Ai/Base/Value/AlwaysLootListValue.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ALWAYSLOOTLISTVALUE_H
|
||||
#define _PLAYERBOT_ALWAYSLOOTLISTVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class AlwaysLootListValue : public ManualSetValue<std::set<uint32>&>
|
||||
{
|
||||
public:
|
||||
AlwaysLootListValue(PlayerbotAI* botAI, std::string const name = "always loot list")
|
||||
: ManualSetValue<std::set<uint32>&>(botAI, list, name)
|
||||
{
|
||||
}
|
||||
|
||||
std::string const Save() override;
|
||||
bool Load(std::string const value) override;
|
||||
|
||||
private:
|
||||
std::set<uint32> list;
|
||||
};
|
||||
|
||||
#endif
|
||||
44
src/Ai/Base/Value/AoeHealValues.cpp
Normal file
44
src/Ai/Base/Value/AoeHealValues.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 "AoeHealValues.h"
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
uint8 AoeHealValue::Calculate()
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 0;
|
||||
|
||||
float range = 0;
|
||||
if (qualifier == "low")
|
||||
range = sPlayerbotAIConfig->lowHealth;
|
||||
else if (qualifier == "medium")
|
||||
range = sPlayerbotAIConfig->mediumHealth;
|
||||
else if (qualifier == "critical")
|
||||
range = sPlayerbotAIConfig->criticalHealth;
|
||||
else if (qualifier == "almost full")
|
||||
range = sPlayerbotAIConfig->almostFullHealth;
|
||||
|
||||
uint8 count = 0;
|
||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(itr->guid);
|
||||
if (!player || !player->IsAlive())
|
||||
continue;
|
||||
|
||||
if (player->GetDistance(bot) >= sPlayerbotAIConfig->sightDistance)
|
||||
continue;
|
||||
|
||||
float percent = (static_cast<float>(player->GetHealth()) / player->GetMaxHealth()) * 100;
|
||||
if (percent <= range)
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
22
src/Ai/Base/Value/AoeHealValues.h
Normal file
22
src/Ai/Base/Value/AoeHealValues.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_AOEHEALVALUES_H
|
||||
#define _PLAYERBOT_AOEHEALVALUES_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class AoeHealValue : public Uint8CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
AoeHealValue(PlayerbotAI* botAI, std::string const name = "aoe heal") : Uint8CalculatedValue(botAI, name) {}
|
||||
|
||||
uint8 Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
160
src/Ai/Base/Value/AoeValues.cpp
Normal file
160
src/Ai/Base/Value/AoeValues.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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 = sServerFacade->GetDistance2d(unit, other);
|
||||
if (sServerFacade->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;
|
||||
}
|
||||
54
src/Ai/Base/Value/AoeValues.h
Normal file
54
src/Ai/Base/Value/AoeValues.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_AOEVALUES_H
|
||||
#define _PLAYERBOT_AOEVALUES_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "GameObject.h"
|
||||
#include "Object.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class AoePositionValue : public CalculatedValue<WorldLocation>
|
||||
{
|
||||
public:
|
||||
AoePositionValue(PlayerbotAI* botAI) : CalculatedValue<WorldLocation>(botAI, "aoe position") {}
|
||||
|
||||
WorldLocation Calculate() override;
|
||||
};
|
||||
|
||||
class AoeCountValue : public CalculatedValue<uint8>
|
||||
{
|
||||
public:
|
||||
AoeCountValue(PlayerbotAI* botAI) : CalculatedValue<uint8>(botAI, "aoe count") {}
|
||||
|
||||
uint8 Calculate() override;
|
||||
};
|
||||
|
||||
class HasAreaDebuffValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
HasAreaDebuffValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI) {}
|
||||
|
||||
Unit* GetTarget()
|
||||
{
|
||||
AiObjectContext* ctx = AiObject::context;
|
||||
|
||||
return ctx->GetValue<Unit*>(qualifier)->Get();
|
||||
}
|
||||
virtual bool Calculate();
|
||||
};
|
||||
|
||||
class AreaDebuffValue : public CalculatedValue<Aura*>
|
||||
{
|
||||
public:
|
||||
AreaDebuffValue(PlayerbotAI* botAI) : CalculatedValue<Aura*>(botAI, "area debuff", 1) {}
|
||||
|
||||
Aura* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
175
src/Ai/Base/Value/Arrow.cpp
Normal file
175
src/Ai/Base/Value/Arrow.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 "Arrow.h"
|
||||
|
||||
#include "Map.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
WorldLocation ArrowFormation::GetLocationInternal()
|
||||
{
|
||||
if (!bot->GetGroup())
|
||||
return Formation::NullLocation;
|
||||
|
||||
Build();
|
||||
|
||||
uint32 tankLines = 1 + tanks.Size() / 6;
|
||||
uint32 meleeLines = 1 + melee.Size() / 6;
|
||||
uint32 rangedLines = 1 + ranged.Size() / 6;
|
||||
uint32 healerLines = 1 + healers.Size() / 6;
|
||||
float offset = 0.f;
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
if (!botAI->IsSafe(master))
|
||||
return Formation::NullLocation;
|
||||
|
||||
float orientation = master->GetOrientation();
|
||||
MultiLineUnitPlacer placer(orientation);
|
||||
|
||||
tanks.PlaceUnits(&placer);
|
||||
tanks.Move(-cos(orientation) * offset, -sin(orientation) * offset);
|
||||
|
||||
offset += tankLines * sPlayerbotAIConfig->followDistance + sPlayerbotAIConfig->tooCloseDistance / 2;
|
||||
melee.PlaceUnits(&placer);
|
||||
melee.Move(-cos(orientation) * offset, -sin(orientation) * offset);
|
||||
|
||||
offset += meleeLines * sPlayerbotAIConfig->followDistance + sPlayerbotAIConfig->tooCloseDistance / 2;
|
||||
ranged.PlaceUnits(&placer);
|
||||
ranged.Move(-cos(orientation) * offset, -sin(orientation) * offset);
|
||||
|
||||
offset += rangedLines * sPlayerbotAIConfig->followDistance;
|
||||
healers.PlaceUnits(&placer);
|
||||
healers.Move(-cos(orientation) * offset, -sin(orientation) * offset);
|
||||
|
||||
if (!masterUnit || !botUnit)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
|
||||
float y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
|
||||
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
if (!master->GetMap()->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||
master->GetPositionZ(), x, y, z))
|
||||
{
|
||||
x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
|
||||
y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
|
||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
master->UpdateAllowedPositionZ(x, y, z);
|
||||
}
|
||||
return WorldLocation(master->GetMapId(), x, y, z);
|
||||
}
|
||||
|
||||
void ArrowFormation::Build()
|
||||
{
|
||||
if (built)
|
||||
return;
|
||||
|
||||
FillSlotsExceptMaster();
|
||||
AddMasterToSlot();
|
||||
|
||||
built = true;
|
||||
}
|
||||
|
||||
FormationSlot* ArrowFormation::FindSlot(Player* member)
|
||||
{
|
||||
if (botAI->IsTank(member))
|
||||
return &tanks;
|
||||
else if (botAI->IsHeal(member))
|
||||
return &healers;
|
||||
else if (botAI->IsRanged(member))
|
||||
return &ranged;
|
||||
else
|
||||
return &melee;
|
||||
}
|
||||
|
||||
void ArrowFormation::FillSlotsExceptMaster()
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
GroupReference* gref = group->GetFirstMember();
|
||||
uint32 index = 0;
|
||||
while (gref)
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (botAI->IsSafe(member))
|
||||
{
|
||||
if (member == bot)
|
||||
FindSlot(member)->AddLast(botUnit = new FormationUnit(index, false));
|
||||
else if (member != botAI->GetMaster())
|
||||
FindSlot(member)->AddLast(new FormationUnit(index, false));
|
||||
++index;
|
||||
}
|
||||
gref = gref->next();
|
||||
}
|
||||
}
|
||||
|
||||
void ArrowFormation::AddMasterToSlot()
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
GroupReference* gref = group->GetFirstMember();
|
||||
uint32 index = 0;
|
||||
while (gref)
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
|
||||
if (member == botAI->GetMaster())
|
||||
{
|
||||
FindSlot(member)->InsertAtCenter(masterUnit = new FormationUnit(index, true));
|
||||
break;
|
||||
}
|
||||
|
||||
gref = gref->next();
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void FormationSlot::PlaceUnits(UnitPlacer* placer)
|
||||
{
|
||||
uint32 index = 0;
|
||||
uint32 count = units.size();
|
||||
for (FormationUnit* unit : units)
|
||||
{
|
||||
unit->SetLocation(placer->Place(unit, index, count));
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
UnitPosition MultiLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint32 count)
|
||||
{
|
||||
SingleLineUnitPlacer placer(orientation);
|
||||
if (count <= 6)
|
||||
return placer.Place(unit, index, count);
|
||||
|
||||
uint32 lineNo = index / 6;
|
||||
uint32 indexInLine = index % 6;
|
||||
uint32 lineSize = std::max(count - lineNo * 6, uint32(6));
|
||||
float x = cos(orientation) * sPlayerbotAIConfig->followDistance * lineNo;
|
||||
float y = sin(orientation) * sPlayerbotAIConfig->followDistance * lineNo;
|
||||
return placer.Place(unit, indexInLine, lineSize);
|
||||
}
|
||||
|
||||
UnitPosition SingleLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint32 count)
|
||||
{
|
||||
float angle = orientation - M_PI / 2.0f;
|
||||
float x = cos(angle) * sPlayerbotAIConfig->followDistance * ((float)index - (float)count / 2);
|
||||
float y = sin(angle) * sPlayerbotAIConfig->followDistance * ((float)index - (float)count / 2);
|
||||
return UnitPosition(x, y);
|
||||
}
|
||||
|
||||
void FormationSlot::Move(float dx, float dy)
|
||||
{
|
||||
for (FormationUnit* unit : units)
|
||||
{
|
||||
unit->SetLocation(unit->GetX() + dx, unit->GetY() + dy);
|
||||
}
|
||||
}
|
||||
|
||||
FormationSlot::~FormationSlot()
|
||||
{
|
||||
for (FormationUnit* unit : units)
|
||||
{
|
||||
delete unit;
|
||||
}
|
||||
|
||||
units.clear();
|
||||
}
|
||||
127
src/Ai/Base/Value/Arrow.h
Normal file
127
src/Ai/Base/Value/Arrow.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ARROW_H
|
||||
#define _PLAYERBOT_ARROW_H
|
||||
|
||||
#include "Formations.h"
|
||||
#include "TravelMgr.h"
|
||||
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
|
||||
class UnitPosition
|
||||
{
|
||||
public:
|
||||
UnitPosition(float x, float y) : x(x), y(y) {}
|
||||
UnitPosition(UnitPosition const& other)
|
||||
{
|
||||
x = other.x;
|
||||
y = other.y;
|
||||
}
|
||||
|
||||
float x, y;
|
||||
};
|
||||
|
||||
class FormationUnit
|
||||
{
|
||||
public:
|
||||
FormationUnit(uint32 groupIndex, bool master) : groupIndex(groupIndex), master(master), position(0, 0) {}
|
||||
FormationUnit(FormationUnit const& other) : position(other.position.x, other.position.y)
|
||||
{
|
||||
groupIndex = other.groupIndex;
|
||||
master = other.master;
|
||||
}
|
||||
|
||||
uint32 GetGroupIdex() { return groupIndex; }
|
||||
void SetLocation(UnitPosition pos) { position = pos; }
|
||||
void SetLocation(float x, float y)
|
||||
{
|
||||
position.x = x;
|
||||
position.y = y;
|
||||
}
|
||||
float GetX() { return position.x; }
|
||||
float GetY() { return position.y; }
|
||||
|
||||
private:
|
||||
uint32 groupIndex;
|
||||
bool master;
|
||||
UnitPosition position;
|
||||
};
|
||||
|
||||
class UnitPlacer
|
||||
{
|
||||
public:
|
||||
UnitPlacer() {}
|
||||
|
||||
virtual UnitPosition Place(FormationUnit* unit, uint32 index, uint32 count) = 0;
|
||||
};
|
||||
|
||||
class FormationSlot
|
||||
{
|
||||
public:
|
||||
FormationSlot() {}
|
||||
virtual ~FormationSlot();
|
||||
|
||||
void AddLast(FormationUnit* unit) { units.push_back(unit); }
|
||||
void InsertAtCenter(FormationUnit* unit) { units.insert(units.begin() + (units.size() + 1) / 2, unit); }
|
||||
void PlaceUnits(UnitPlacer* placer);
|
||||
void Move(float dx, float dy);
|
||||
uint32 Size() const { return units.size(); }
|
||||
|
||||
private:
|
||||
WorldLocation center;
|
||||
std::vector<FormationUnit*> units;
|
||||
};
|
||||
|
||||
class MultiLineUnitPlacer : public UnitPlacer
|
||||
{
|
||||
public:
|
||||
MultiLineUnitPlacer(float orientation) : UnitPlacer(), orientation(orientation) {}
|
||||
|
||||
UnitPosition Place(FormationUnit* unit, uint32 index, uint32 count) override;
|
||||
|
||||
private:
|
||||
float orientation;
|
||||
};
|
||||
|
||||
class SingleLineUnitPlacer
|
||||
{
|
||||
public:
|
||||
SingleLineUnitPlacer(float orientation) : orientation(orientation) {}
|
||||
|
||||
virtual UnitPosition Place(FormationUnit* unit, uint32 index, uint32 count);
|
||||
|
||||
private:
|
||||
float orientation;
|
||||
};
|
||||
|
||||
class ArrowFormation : public MoveAheadFormation
|
||||
{
|
||||
public:
|
||||
ArrowFormation(PlayerbotAI* botAI)
|
||||
: MoveAheadFormation(botAI, "arrow"), built(false), masterUnit(nullptr), botUnit(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
WorldLocation GetLocationInternal() override;
|
||||
|
||||
private:
|
||||
void Build();
|
||||
void FillSlotsExceptMaster();
|
||||
void AddMasterToSlot();
|
||||
FormationSlot* FindSlot(Player* member);
|
||||
|
||||
private:
|
||||
FormationSlot tanks;
|
||||
FormationSlot melee;
|
||||
FormationSlot ranged;
|
||||
FormationSlot healers;
|
||||
FormationUnit* masterUnit;
|
||||
FormationUnit* botUnit;
|
||||
bool built;
|
||||
};
|
||||
|
||||
#endif
|
||||
133
src/Ai/Base/Value/AttackerCountValues.cpp
Normal file
133
src/Ai/Base/Value/AttackerCountValues.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 "AttackerCountValues.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
uint8 MyAttackerCountValue::Calculate() { return bot->getAttackers().size(); }
|
||||
|
||||
bool HasAggroValue::Calculate()
|
||||
{
|
||||
Unit* target = GetTarget();
|
||||
if (!target)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Unit* victim = target->GetVictim();
|
||||
if (!victim)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool isMT = botAI->IsMainTank(bot);
|
||||
if (victim &&
|
||||
(victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && botAI->IsTank(victim->ToPlayer()))))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 AttackerCountValue::Calculate()
|
||||
{
|
||||
uint32 count = 0;
|
||||
float range = sPlayerbotAIConfig->sightDistance;
|
||||
|
||||
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
|
||||
for (ObjectGuid const guid : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
|
||||
float distance = bot->GetDistance(unit);
|
||||
if (distance <= range)
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8 BalancePercentValue::Calculate()
|
||||
{
|
||||
float playerLevel = 0, attackerLevel = 0;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(itr->guid);
|
||||
if (!player || !player->IsAlive())
|
||||
continue;
|
||||
|
||||
playerLevel += player->GetLevel();
|
||||
}
|
||||
uint32 memberCount = group->GetMembersCount();
|
||||
playerLevel /= memberCount;
|
||||
if (memberCount <= 10)
|
||||
playerLevel *= memberCount;
|
||||
else
|
||||
playerLevel *= 10;
|
||||
}
|
||||
GuidVector v = context->GetValue<GuidVector>("attackers")->Get();
|
||||
for (ObjectGuid const guid : v)
|
||||
{
|
||||
Creature* creature = botAI->GetCreature((guid));
|
||||
if (!creature || !creature->IsAlive())
|
||||
continue;
|
||||
|
||||
uint32 level = creature->GetLevel();
|
||||
|
||||
switch (creature->GetCreatureTemplate()->rank)
|
||||
{
|
||||
case CREATURE_ELITE_RARE:
|
||||
level *= 2;
|
||||
break;
|
||||
case CREATURE_ELITE_ELITE:
|
||||
level *= 3;
|
||||
break;
|
||||
case CREATURE_ELITE_RAREELITE:
|
||||
level *= 3;
|
||||
break;
|
||||
case CREATURE_ELITE_WORLDBOSS:
|
||||
level *= 20;
|
||||
break;
|
||||
}
|
||||
|
||||
attackerLevel += level;
|
||||
}
|
||||
|
||||
if (!attackerLevel)
|
||||
return 100;
|
||||
|
||||
float percent = playerLevel * 100 / attackerLevel;
|
||||
return percent <= 200 ? (uint8)percent : 200;
|
||||
}
|
||||
|
||||
Unit* AttackerCountValue::GetTarget()
|
||||
{
|
||||
AiObjectContext* ctx = AiObject::context;
|
||||
return ctx->GetValue<Unit*>(qualifier)->Get();
|
||||
}
|
||||
|
||||
Unit* MyAttackerCountValue::GetTarget()
|
||||
{
|
||||
AiObjectContext* ctx = AiObject::context;
|
||||
return ctx->GetValue<Unit*>(qualifier)->Get();
|
||||
}
|
||||
|
||||
Unit* HasAggroValue::GetTarget()
|
||||
{
|
||||
AiObjectContext* ctx = AiObject::context;
|
||||
return ctx->GetValue<Unit*>(qualifier)->Get();
|
||||
}
|
||||
|
||||
Unit* BalancePercentValue::GetTarget()
|
||||
{
|
||||
AiObjectContext* ctx = AiObject::context;
|
||||
return ctx->GetValue<Unit*>(qualifier)->Get();
|
||||
}
|
||||
60
src/Ai/Base/Value/AttackerCountValues.h
Normal file
60
src/Ai/Base/Value/AttackerCountValues.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ATTACKERCOUNTVALUES_H
|
||||
#define _PLAYERBOT_ATTACKERCOUNTVALUES_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class AttackerCountValue : public Uint8CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
AttackerCountValue(PlayerbotAI* botAI, std::string const name = "attackers count")
|
||||
: Uint8CalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* GetTarget();
|
||||
uint8 Calculate() override;
|
||||
};
|
||||
|
||||
class MyAttackerCountValue : public Uint8CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
MyAttackerCountValue(PlayerbotAI* botAI, std::string const name = "my attackers count")
|
||||
: Uint8CalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* GetTarget();
|
||||
uint8 Calculate() override;
|
||||
};
|
||||
|
||||
class HasAggroValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
HasAggroValue(PlayerbotAI* botAI, std::string const name = "has aggro") : BoolCalculatedValue(botAI, name) {}
|
||||
|
||||
Unit* GetTarget();
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class BalancePercentValue : public Uint8CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
BalancePercentValue(PlayerbotAI* botAI, std::string const name = "balance percentage")
|
||||
: Uint8CalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* GetTarget();
|
||||
uint8 Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
71
src/Ai/Base/Value/AttackerWithoutAuraTargetValue.cpp
Normal file
71
src/Ai/Base/Value/AttackerWithoutAuraTargetValue.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 "AttackerWithoutAuraTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
Unit* AttackerWithoutAuraTargetValue::Calculate()
|
||||
{
|
||||
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
|
||||
// Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||
uint32 max_health = 0;
|
||||
Unit* result = nullptr;
|
||||
for (ObjectGuid const guid : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (!bot->IsWithinCombatRange(unit, botAI->GetRange(range)))
|
||||
continue;
|
||||
|
||||
if (unit->GetHealth() < max_health)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!botAI->HasAura(qualifier, unit, false, true))
|
||||
{
|
||||
max_health = unit->GetHealth();
|
||||
result = unit;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Unit* MeleeAttackerWithoutAuraTargetValue::Calculate()
|
||||
{
|
||||
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
|
||||
// Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||
uint32 max_health = 0;
|
||||
Unit* result = nullptr;
|
||||
for (ObjectGuid const guid : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || !unit->IsAlive())
|
||||
continue;
|
||||
|
||||
if (!bot->IsWithinMeleeRange(unit))
|
||||
continue;
|
||||
|
||||
if (checkArc && !bot->HasInArc(CAST_ANGLE_IN_FRONT, unit))
|
||||
continue;
|
||||
|
||||
if (unit->GetHealth() < max_health)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!botAI->HasAura(qualifier, unit, false, true))
|
||||
{
|
||||
max_health = unit->GetHealth();
|
||||
result = unit;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
36
src/Ai/Base/Value/AttackerWithoutAuraTargetValue.h
Normal file
36
src/Ai/Base/Value/AttackerWithoutAuraTargetValue.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ATTACKERWITHOUTAURATARGETVALUE_H
|
||||
#define _PLAYERBOT_ATTACKERWITHOUTAURATARGETVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class AttackerWithoutAuraTargetValue : public UnitCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
AttackerWithoutAuraTargetValue(PlayerbotAI* botAI, std::string range = "spell")
|
||||
: UnitCalculatedValue(botAI, "attacker without aura"), range(range)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
Unit* Calculate() override;
|
||||
std::string range;
|
||||
};
|
||||
|
||||
class MeleeAttackerWithoutAuraTargetValue : public AttackerWithoutAuraTargetValue
|
||||
{
|
||||
public:
|
||||
MeleeAttackerWithoutAuraTargetValue(PlayerbotAI* botAI, bool checkArc = true) : AttackerWithoutAuraTargetValue(botAI, "melee"), checkArc(checkArc) {}
|
||||
Unit* Calculate() override;
|
||||
bool checkArc;
|
||||
};
|
||||
|
||||
#endif
|
||||
281
src/Ai/Base/Value/AttackersValue.cpp
Normal file
281
src/Ai/Base/Value/AttackersValue.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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 "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<Unit*> 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<Unit*>& 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<Unit*>& units) : units(units) {}
|
||||
|
||||
void operator()(Unit* target) const { units.push_back(target); }
|
||||
|
||||
std::vector<Unit*>& units;
|
||||
};
|
||||
|
||||
void AttackersValue::AddAttackersOf(Player* player, std::unordered_set<Unit*>& 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<Unit*>& targets)
|
||||
{
|
||||
for (std::unordered_set<Unit*>::iterator tIter = targets.begin(); tIter != targets.end();)
|
||||
{
|
||||
Unit* unit = *tIter;
|
||||
if (bot->GetMapId() != unit->GetMapId() || !hasRealThreat(unit) || !IsValidTarget(unit, bot))
|
||||
{
|
||||
std::unordered_set<Unit*>::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<Player*>(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<Unit*>("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<Creature*>(*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<ObjectGuid>("pull target")->Get());
|
||||
}
|
||||
|
||||
bool PossibleAddsValue::Calculate()
|
||||
{
|
||||
GuidVector possible = botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get();
|
||||
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("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;
|
||||
}
|
||||
52
src/Ai/Base/Value/AttackersValue.h
Normal file
52
src/Ai/Base/Value/AttackersValue.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ATTACKERSVALUE_H
|
||||
#define _PLAYERBOT_ATTACKERSVALUE_H
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Value.h"
|
||||
|
||||
class Group;
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class AttackersValue : public ObjectGuidListCalculatedValue
|
||||
{
|
||||
public:
|
||||
AttackersValue(PlayerbotAI* botAI) : ObjectGuidListCalculatedValue(botAI, "attackers", 1 * 1000) {}
|
||||
|
||||
GuidVector Calculate();
|
||||
static bool IsPossibleTarget(Unit* attacker, Player* bot, float range = sPlayerbotAIConfig->sightDistance);
|
||||
static bool IsValidTarget(Unit* attacker, Player* bot);
|
||||
|
||||
private:
|
||||
void AddAttackersOf(Group* group, std::unordered_set<Unit*>& targets);
|
||||
void AddAttackersOf(Player* player, std::unordered_set<Unit*>& targets);
|
||||
void RemoveNonThreating(std::unordered_set<Unit*>& targets);
|
||||
bool hasRealThreat(Unit* attacker);
|
||||
};
|
||||
|
||||
class PossibleAddsValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
PossibleAddsValue(PlayerbotAI* botAI, std::string const name = "possible adds") : BoolCalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class PrioritizedTargetsValue : public ManualSetValue<GuidVector>
|
||||
{
|
||||
public:
|
||||
PrioritizedTargetsValue(PlayerbotAI* botAI, std::string const name = "prioritized targets")
|
||||
: ManualSetValue(botAI, GuidVector(), name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
30
src/Ai/Base/Value/AvailableLootValue.cpp
Normal file
30
src/Ai/Base/Value/AvailableLootValue.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 "AvailableLootValue.h"
|
||||
|
||||
#include "LootObjectStack.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
AvailableLootValue::AvailableLootValue(PlayerbotAI* botAI, std::string const name)
|
||||
: ManualSetValue<LootObjectStack*>(botAI, nullptr, name)
|
||||
{
|
||||
value = new LootObjectStack(botAI->GetBot());
|
||||
}
|
||||
|
||||
AvailableLootValue::~AvailableLootValue() { delete value; }
|
||||
|
||||
LootTargetValue::LootTargetValue(PlayerbotAI* botAI, std::string const name)
|
||||
: ManualSetValue<LootObject>(botAI, LootObject(), name)
|
||||
{
|
||||
}
|
||||
|
||||
bool CanLootValue::Calculate()
|
||||
{
|
||||
LootObject loot = AI_VALUE(LootObject, "loot target");
|
||||
return !loot.IsEmpty() && loot.GetWorldObject(bot) && loot.IsLootPossible(bot) &&
|
||||
sServerFacade->IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", "loot target"), INTERACTION_DISTANCE - 2);
|
||||
}
|
||||
35
src/Ai/Base/Value/AvailableLootValue.h
Normal file
35
src/Ai/Base/Value/AvailableLootValue.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_AVAILABLELOOTVALUE_H
|
||||
#define _PLAYERBOT_AVAILABLELOOTVALUE_H
|
||||
|
||||
#include "LootObjectStack.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class AvailableLootValue : public ManualSetValue<LootObjectStack*>
|
||||
{
|
||||
public:
|
||||
AvailableLootValue(PlayerbotAI* botAI, std::string const name = "available loot");
|
||||
virtual ~AvailableLootValue();
|
||||
};
|
||||
|
||||
class LootTargetValue : public ManualSetValue<LootObject>
|
||||
{
|
||||
public:
|
||||
LootTargetValue(PlayerbotAI* botAI, std::string const name = "loot target");
|
||||
};
|
||||
|
||||
class CanLootValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanLootValue(PlayerbotAI* botAI, std::string const name = "can loot") : BoolCalculatedValue(botAI, name) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
234
src/Ai/Base/Value/BudgetValues.cpp
Normal file
234
src/Ai/Base/Value/BudgetValues.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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 "BudgetValues.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
uint32 MaxGearRepairCostValue::Calculate()
|
||||
{
|
||||
uint32 TotalCost = 0;
|
||||
for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i);
|
||||
Item* item = bot->GetItemByPos(pos);
|
||||
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
|
||||
if (!maxDurability)
|
||||
continue;
|
||||
|
||||
uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
|
||||
|
||||
if (i >= EQUIPMENT_SLOT_END && curDurability >= maxDurability) // Only count items equiped or already damanged.
|
||||
continue;
|
||||
|
||||
ItemTemplate const* ditemProto = item->GetTemplate();
|
||||
|
||||
DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
|
||||
if (!dcost)
|
||||
continue;
|
||||
|
||||
uint32 dQualitymodEntryId = (ditemProto->Quality + 1) * 2;
|
||||
DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
|
||||
if (!dQualitymodEntry)
|
||||
continue;
|
||||
|
||||
uint32 dmultiplier =
|
||||
dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)];
|
||||
|
||||
uint32 costs = uint32(maxDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
|
||||
|
||||
TotalCost += costs;
|
||||
}
|
||||
|
||||
return TotalCost;
|
||||
}
|
||||
|
||||
uint32 RepairCostValue::Calculate()
|
||||
{
|
||||
uint32 TotalCost = 0;
|
||||
for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i)
|
||||
{
|
||||
uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i);
|
||||
Item* item = bot->GetItemByPos(pos);
|
||||
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
uint32 maxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY);
|
||||
if (!maxDurability)
|
||||
continue;
|
||||
|
||||
uint32 curDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY);
|
||||
|
||||
uint32 LostDurability = maxDurability - curDurability;
|
||||
|
||||
if (LostDurability == 0)
|
||||
continue;
|
||||
|
||||
ItemTemplate const* ditemProto = item->GetTemplate();
|
||||
|
||||
DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
|
||||
if (!dcost)
|
||||
continue;
|
||||
|
||||
uint32 dQualitymodEntryId = (ditemProto->Quality + 1) * 2;
|
||||
DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
|
||||
if (!dQualitymodEntry)
|
||||
continue;
|
||||
|
||||
uint32 dmultiplier =
|
||||
dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class, ditemProto->SubClass)];
|
||||
uint32 costs = uint32(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod));
|
||||
|
||||
TotalCost += costs;
|
||||
}
|
||||
|
||||
return TotalCost;
|
||||
}
|
||||
|
||||
uint32 TrainCostValue::Calculate()
|
||||
{
|
||||
uint32 TotalCost = 0;
|
||||
|
||||
std::set<uint32> spells;
|
||||
|
||||
if (CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates())
|
||||
{
|
||||
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||
{
|
||||
Trainer::Trainer* trainer = sObjectMgr->GetTrainer(itr->first);
|
||||
|
||||
if (!trainer)
|
||||
continue;
|
||||
|
||||
if (trainer->GetTrainerType() != Trainer::Type::Class || !trainer->IsTrainerValidForPlayer(bot))
|
||||
continue;
|
||||
|
||||
for (auto& spell : trainer->GetSpells())
|
||||
{
|
||||
if (!trainer->CanTeachSpell(bot, trainer->GetSpell(spell.SpellId)))
|
||||
continue;
|
||||
|
||||
if (spells.find(spell.SpellId) != spells.end())
|
||||
continue;
|
||||
|
||||
TotalCost += spell.MoneyCost;
|
||||
spells.insert(spell.SpellId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TotalCost;
|
||||
}
|
||||
|
||||
uint32 MoneyNeededForValue::Calculate()
|
||||
{
|
||||
NeedMoneyFor needMoneyFor = NeedMoneyFor(stoi(getQualifier()));
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||
|
||||
uint32 moneyWanted = 0;
|
||||
|
||||
uint32 level = bot->GetLevel();
|
||||
|
||||
switch (needMoneyFor)
|
||||
{
|
||||
case NeedMoneyFor::none:
|
||||
moneyWanted = 0;
|
||||
break;
|
||||
case NeedMoneyFor::repair:
|
||||
moneyWanted = AI_VALUE(uint32, "max repair cost");
|
||||
break;
|
||||
case NeedMoneyFor::ammo:
|
||||
moneyWanted = (bot->getClass() == CLASS_HUNTER)
|
||||
? (level * level * level) / 10
|
||||
: 0; // Or level^3 (1s @ lvl10, 30s @ lvl30, 2g @ lvl60, 5g @ lvl80): Todo replace
|
||||
// (should be best ammo buyable x 8 stacks cost)
|
||||
break;
|
||||
case NeedMoneyFor::spells:
|
||||
moneyWanted = AI_VALUE(uint32, "train cost");
|
||||
break;
|
||||
case NeedMoneyFor::travel:
|
||||
moneyWanted =
|
||||
bot->isTaxiCheater()
|
||||
? 0
|
||||
: 1500; // 15s for traveling half a continent. Todo: Add better calculation (Should be ???)
|
||||
break;
|
||||
case NeedMoneyFor::gear:
|
||||
moneyWanted = level * level * level; // Or level^3 (10s @ lvl10, 3g @ lvl30, 20g @ lvl60, 50g @ lvl80):
|
||||
// Todo replace (Should be ~total cost of all >green gear equiped)
|
||||
break;
|
||||
case NeedMoneyFor::consumables:
|
||||
moneyWanted =
|
||||
(level * level * level) / 10; // Or level^3 (1s @ lvl10, 30s @ lvl30, 2g @ lvl60, 5g @ lvl80): Todo
|
||||
// replace (Should be best food/drink x 2 stacks cost)
|
||||
break;
|
||||
case NeedMoneyFor::guild:
|
||||
if (botAI->HasStrategy("guild", BOT_STATE_NON_COMBAT))
|
||||
{
|
||||
if (bot->GetGuildId())
|
||||
moneyWanted = AI_VALUE2(uint32, "item count", chat->FormatQItem(5976)) ? 0 : 10000; // 1g (tabard)
|
||||
else
|
||||
moneyWanted =
|
||||
AI_VALUE2(uint32, "item count", chat->FormatQItem(5863)) ? 0 : 10000; // 10s (guild charter)
|
||||
}
|
||||
break;
|
||||
case NeedMoneyFor::tradeskill:
|
||||
moneyWanted = (level * level *
|
||||
level); // Or level^3 (10s @ lvl10, 3g @ lvl30, 20g @ lvl60, 50g @ lvl80): Todo replace
|
||||
// (Should be buyable reagents that combined allow crafting of usefull items)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return moneyWanted;
|
||||
};
|
||||
|
||||
uint32 TotalMoneyNeededForValue::Calculate()
|
||||
{
|
||||
NeedMoneyFor needMoneyFor = NeedMoneyFor(stoi(getQualifier()));
|
||||
|
||||
uint32 moneyWanted = AI_VALUE2(uint32, "money needed for", (uint32)needMoneyFor);
|
||||
|
||||
auto needPtr = std::find(saveMoneyFor.begin(), saveMoneyFor.end(), needMoneyFor);
|
||||
|
||||
while (needPtr != saveMoneyFor.begin())
|
||||
{
|
||||
needPtr--;
|
||||
|
||||
NeedMoneyFor alsoNeed = *needPtr;
|
||||
|
||||
moneyWanted = moneyWanted + AI_VALUE2(uint32, "money needed for", (uint32)alsoNeed);
|
||||
}
|
||||
|
||||
return moneyWanted;
|
||||
}
|
||||
|
||||
uint32 FreeMoneyForValue::Calculate()
|
||||
{
|
||||
uint32 money = bot->GetMoney();
|
||||
|
||||
if (botAI->HasCheat(BotCheatMask::gold))
|
||||
return 10000000;
|
||||
|
||||
if (botAI->HasActivePlayerMaster())
|
||||
return money;
|
||||
|
||||
uint32 savedMoney = AI_VALUE2(uint32, "total money needed for", getQualifier()) -
|
||||
AI_VALUE2(uint32, "money needed for", getQualifier());
|
||||
|
||||
if (savedMoney > money)
|
||||
return 0;
|
||||
|
||||
return money - savedMoney;
|
||||
};
|
||||
|
||||
bool ShouldGetMoneyValue::Calculate() { return !AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::anything); };
|
||||
88
src/Ai/Base/Value/BudgetValues.h
Normal file
88
src/Ai/Base/Value/BudgetValues.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_BUDGETVALUES_H
|
||||
#define _PLAYERBOT_BUDGETVALUES_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
enum class NeedMoneyFor : uint32
|
||||
{
|
||||
none = 0,
|
||||
repair = 1,
|
||||
ammo = 2,
|
||||
spells = 3,
|
||||
travel = 4,
|
||||
consumables = 5,
|
||||
gear = 6,
|
||||
guild = 7,
|
||||
tradeskill = 8,
|
||||
anything = 9
|
||||
};
|
||||
|
||||
class MaxGearRepairCostValue : public Uint32CalculatedValue
|
||||
{
|
||||
public:
|
||||
MaxGearRepairCostValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "max repair cost", 60) {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class RepairCostValue : public Uint32CalculatedValue
|
||||
{
|
||||
public:
|
||||
RepairCostValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "repair cost", 60) {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class TrainCostValue : public Uint32CalculatedValue
|
||||
{
|
||||
public:
|
||||
TrainCostValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "train cost", 60) {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class MoneyNeededForValue : public Uint32CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
MoneyNeededForValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "money needed for", 60) {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class TotalMoneyNeededForValue : public Uint32CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
TotalMoneyNeededForValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "total money needed for", 60) {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
|
||||
private:
|
||||
std::vector<NeedMoneyFor> saveMoneyFor = {NeedMoneyFor::guild, NeedMoneyFor::repair, NeedMoneyFor::ammo,
|
||||
NeedMoneyFor::spells, NeedMoneyFor::travel};
|
||||
};
|
||||
|
||||
class FreeMoneyForValue : public Uint32CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
FreeMoneyForValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "free money for") {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class ShouldGetMoneyValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
ShouldGetMoneyValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "should get money", 2) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
94
src/Ai/Base/Value/CcTargetValue.cpp
Normal file
94
src/Ai/Base/Value/CcTargetValue.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 "CcTargetValue.h"
|
||||
|
||||
#include "Action.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
class FindTargetForCcStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindTargetForCcStrategy(PlayerbotAI* botAI, std::string const spell)
|
||||
: FindTargetStrategy(botAI), spell(spell), maxDistance(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
if (!botAI->CanCastSpell(spell, creature))
|
||||
return;
|
||||
|
||||
if (*botAI->GetAiObjectContext()->GetValue<Unit*>("rti cc target") == creature)
|
||||
{
|
||||
result = creature;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*botAI->GetAiObjectContext()->GetValue<Unit*>("current target") == creature)
|
||||
return;
|
||||
|
||||
uint8 health = static_cast<uint8>(creature->GetHealthPct());
|
||||
if (health < sPlayerbotAIConfig->mediumHealth)
|
||||
return;
|
||||
|
||||
float minDistance = botAI->GetRange("spell");
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
if (*botAI->GetAiObjectContext()->GetValue<uint8>("aoe count") > 2)
|
||||
{
|
||||
WorldLocation aoe = *botAI->GetAiObjectContext()->GetValue<WorldLocation>("aoe position");
|
||||
if (sServerFacade->IsDistanceLessOrEqualThan(
|
||||
sServerFacade->GetDistance2d(creature, aoe.GetPositionX(), aoe.GetPositionY()),
|
||||
sPlayerbotAIConfig->aoeRadius))
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 tankCount = 0;
|
||||
uint32 dpsCount = 0;
|
||||
GetPlayerCount(creature, &tankCount, &dpsCount);
|
||||
if (!tankCount || !dpsCount)
|
||||
{
|
||||
result = creature;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!botAI->IsTank(member))
|
||||
continue;
|
||||
|
||||
float distance = sServerFacade->GetDistance2d(member, creature);
|
||||
if (distance < minDistance)
|
||||
minDistance = distance;
|
||||
}
|
||||
|
||||
if (!result || minDistance > maxDistance)
|
||||
{
|
||||
result = creature;
|
||||
maxDistance = minDistance;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const spell;
|
||||
float maxDistance;
|
||||
};
|
||||
|
||||
Unit* CcTargetValue::Calculate()
|
||||
{
|
||||
FindTargetForCcStrategy strategy(botAI, qualifier);
|
||||
return FindTarget(&strategy);
|
||||
}
|
||||
23
src/Ai/Base/Value/CcTargetValue.h
Normal file
23
src/Ai/Base/Value/CcTargetValue.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CCTARGETVALUE_H
|
||||
#define _PLAYERBOT_CCTARGETVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class CcTargetValue : public TargetValue, public Qualified
|
||||
{
|
||||
public:
|
||||
CcTargetValue(PlayerbotAI* botAI, std::string const name = "cc target") : TargetValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
24
src/Ai/Base/Value/ChatValue.h
Normal file
24
src/Ai/Base/Value/ChatValue.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CHATVALUE_H
|
||||
#define _PLAYERBOT_CHATVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
enum ChatMsg : uint32;
|
||||
|
||||
class ChatValue : public ManualSetValue<ChatMsg>
|
||||
{
|
||||
public:
|
||||
ChatValue(PlayerbotAI* botAI, std::string const name = "chat")
|
||||
: ManualSetValue<ChatMsg>(botAI, CHAT_MSG_WHISPER, name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
37
src/Ai/Base/Value/CollisionValue.cpp
Normal file
37
src/Ai/Base/Value/CollisionValue.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "CollisionValue.h"
|
||||
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
bool CollisionValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
std::list<Unit*> targets;
|
||||
float range = sPlayerbotAIConfig->contactDistance;
|
||||
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
|
||||
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||
Cell::VisitObjects(bot, searcher, range);
|
||||
|
||||
for (Unit* target : targets)
|
||||
{
|
||||
if (bot == target)
|
||||
continue;
|
||||
|
||||
float dist = sServerFacade->GetDistance2d(bot, target->GetPositionX(), target->GetPositionY());
|
||||
if (sServerFacade->IsDistanceLessThan(dist, target->GetCombatReach()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
25
src/Ai/Base/Value/CollisionValue.h
Normal file
25
src/Ai/Base/Value/CollisionValue.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_COLLISIONVALUE_H
|
||||
#define _PLAYERBOT_COLLISIONVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class CollisionValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
CollisionValue(PlayerbotAI* botAI, std::string const name = "collision")
|
||||
: BoolCalculatedValue(botAI, name), Qualified()
|
||||
{
|
||||
}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
72
src/Ai/Base/Value/CraftValue.h
Normal file
72
src/Ai/Base/Value/CraftValue.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CRAFTVALUE_H
|
||||
#define _PLAYERBOT_CRAFTVALUE_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class CraftData
|
||||
{
|
||||
public:
|
||||
CraftData() : itemId(0) {}
|
||||
CraftData(CraftData const& other) : itemId(other.itemId)
|
||||
{
|
||||
required.insert(other.required.begin(), other.required.end());
|
||||
obtained.insert(other.obtained.begin(), other.obtained.end());
|
||||
}
|
||||
|
||||
uint32 itemId;
|
||||
std::map<uint32, uint32> required, obtained;
|
||||
|
||||
bool IsEmpty() { return itemId == 0; }
|
||||
void Reset() { itemId = 0; }
|
||||
bool IsRequired(uint32 item) { return required.find(item) != required.end(); }
|
||||
bool IsFulfilled()
|
||||
{
|
||||
for (std::map<uint32, uint32>::iterator i = required.begin(); i != required.end(); ++i)
|
||||
{
|
||||
uint32 item = i->first;
|
||||
if (obtained[item] < i->second)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
void AddObtained(uint32 itemId, uint32 count)
|
||||
{
|
||||
if (IsRequired(itemId))
|
||||
{
|
||||
obtained[itemId] += count;
|
||||
}
|
||||
}
|
||||
|
||||
void Crafted(uint32 count)
|
||||
{
|
||||
for (std::map<uint32, uint32>::iterator i = required.begin(); i != required.end(); ++i)
|
||||
{
|
||||
uint32 item = i->first;
|
||||
if (obtained[item] >= required[item] * count)
|
||||
{
|
||||
obtained[item] -= required[item] * count;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CraftValue : public ManualSetValue<CraftData&>
|
||||
{
|
||||
public:
|
||||
CraftValue(PlayerbotAI* botAI, std::string const name = "craft") : ManualSetValue<CraftData&>(botAI, data, name) {}
|
||||
|
||||
private:
|
||||
CraftData data;
|
||||
};
|
||||
|
||||
#endif
|
||||
31
src/Ai/Base/Value/CurrentCcTargetValue.cpp
Normal file
31
src/Ai/Base/Value/CurrentCcTargetValue.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 "CurrentCcTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
class FindCurrentCcTargetStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindCurrentCcTargetStrategy(PlayerbotAI* botAI, std::string const spell) : FindTargetStrategy(botAI), spell(spell)
|
||||
{
|
||||
}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
if (botAI->HasAura(spell, attacker))
|
||||
result = attacker;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string const spell;
|
||||
};
|
||||
|
||||
Unit* CurrentCcTargetValue::Calculate()
|
||||
{
|
||||
FindCurrentCcTargetStrategy strategy(botAI, qualifier);
|
||||
return FindTarget(&strategy);
|
||||
}
|
||||
23
src/Ai/Base/Value/CurrentCcTargetValue.h
Normal file
23
src/Ai/Base/Value/CurrentCcTargetValue.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CURRENTCCTARGETVALUE_H
|
||||
#define _PLAYERBOT_CURRENTCCTARGETVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class CurrentCcTargetValue : public TargetValue, public Qualified
|
||||
{
|
||||
public:
|
||||
CurrentCcTargetValue(PlayerbotAI* botAI, std::string const name = "current cc target") : TargetValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
22
src/Ai/Base/Value/CurrentTargetValue.cpp
Normal file
22
src/Ai/Base/Value/CurrentTargetValue.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 "CurrentTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
Unit* CurrentTargetValue::Get()
|
||||
{
|
||||
if (selection.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
Unit* unit = ObjectAccessor::GetUnit(*bot, selection);
|
||||
// if (unit && !bot->IsWithinLOSInMap(unit))
|
||||
// return nullptr;
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
void CurrentTargetValue::Set(Unit* target) { selection = target ? target->GetGUID() : ObjectGuid::Empty; }
|
||||
29
src/Ai/Base/Value/CurrentTargetValue.h
Normal file
29
src/Ai/Base/Value/CurrentTargetValue.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_CURRENTTARGETVALUE_H
|
||||
#define _PLAYERBOT_CURRENTTARGETVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class CurrentTargetValue : public UnitManualSetValue
|
||||
{
|
||||
public:
|
||||
CurrentTargetValue(PlayerbotAI* botAI, std::string const name = "current target")
|
||||
: UnitManualSetValue(botAI, nullptr, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* Get() override;
|
||||
void Set(Unit* unit) override;
|
||||
|
||||
private:
|
||||
ObjectGuid selection;
|
||||
};
|
||||
|
||||
#endif
|
||||
100
src/Ai/Base/Value/DistanceValue.cpp
Normal file
100
src/Ai/Base/Value/DistanceValue.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 "DistanceValue.h"
|
||||
|
||||
#include "Formations.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PositionValue.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "Stances.h"
|
||||
|
||||
float DistanceValue::Calculate()
|
||||
{
|
||||
if (qualifier == "loot target")
|
||||
{
|
||||
LootObject loot = AI_VALUE(LootObject, qualifier);
|
||||
if (loot.IsEmpty())
|
||||
return 0.0f;
|
||||
|
||||
WorldObject* obj = loot.GetWorldObject(bot);
|
||||
if (!obj || !obj->IsInWorld())
|
||||
return 0.0f;
|
||||
|
||||
return sServerFacade->GetDistance2d(botAI->GetBot(), obj);
|
||||
}
|
||||
|
||||
if (qualifier.find("position_") == 0)
|
||||
{
|
||||
std::string const position = qualifier.substr(9);
|
||||
PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()[position];
|
||||
if (!pos.isSet())
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->GetBot()->GetMapId() != pos.mapId)
|
||||
return 0.0f;
|
||||
|
||||
return sServerFacade->GetDistance2d(botAI->GetBot(), pos.x, pos.y);
|
||||
}
|
||||
|
||||
Unit* target = nullptr;
|
||||
if (qualifier == "rpg target")
|
||||
{
|
||||
GuidPosition rpgTarget = AI_VALUE(GuidPosition, qualifier);
|
||||
return rpgTarget.distance(bot);
|
||||
}
|
||||
else if (qualifier == "travel target")
|
||||
{
|
||||
TravelTarget* travelTarget = AI_VALUE(TravelTarget*, qualifier);
|
||||
return travelTarget->distance(botAI->GetBot());
|
||||
}
|
||||
else if (qualifier == "last long move")
|
||||
{
|
||||
WorldPosition target = AI_VALUE(WorldPosition, qualifier);
|
||||
return target.distance(botAI->GetBot());
|
||||
}
|
||||
else if (qualifier == "home bind")
|
||||
{
|
||||
WorldPosition target = AI_VALUE(WorldPosition, qualifier);
|
||||
return target.distance(botAI->GetBot());
|
||||
}
|
||||
else if (qualifier == "current target")
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
if (!target || !target->IsInWorld())
|
||||
return 0.0f;
|
||||
|
||||
return bot->GetDistance2d(target);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = AI_VALUE(Unit*, qualifier);
|
||||
if (target && target == GetMaster() && target != bot)
|
||||
{
|
||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||
WorldLocation loc = formation->GetLocation();
|
||||
return sServerFacade->GetDistance2d(botAI->GetBot(), loc.GetPositionX(), loc.GetPositionY());
|
||||
}
|
||||
}
|
||||
|
||||
if (!target || !target->IsInWorld())
|
||||
return 0.0f;
|
||||
|
||||
if (target == botAI->GetBot())
|
||||
return 0.0f;
|
||||
|
||||
return sServerFacade->GetDistance2d(botAI->GetBot(), target);
|
||||
}
|
||||
|
||||
bool InsideTargetValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
if (!target || !target->IsInWorld() || target == botAI->GetBot())
|
||||
return false;
|
||||
|
||||
float dist = sServerFacade->GetDistance2d(botAI->GetBot(), target->GetPositionX(), target->GetPositionY());
|
||||
return sServerFacade->IsDistanceLessThan(dist, target->GetCombatReach());
|
||||
}
|
||||
32
src/Ai/Base/Value/DistanceValue.h
Normal file
32
src/Ai/Base/Value/DistanceValue.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_DISTANCEVALUE_H
|
||||
#define _PLAYERBOT_DISTANCEVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class DistanceValue : public FloatCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
DistanceValue(PlayerbotAI* botAI, std::string const name = "distance") : FloatCalculatedValue(botAI, name) {}
|
||||
|
||||
float Calculate() override;
|
||||
};
|
||||
|
||||
class InsideTargetValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
InsideTargetValue(PlayerbotAI* botAI, std::string const name = "inside target") : BoolCalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
350
src/Ai/Base/Value/DpsTargetValue.cpp
Normal file
350
src/Ai/Base/Value/DpsTargetValue.cpp
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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 "DpsTargetValue.h"
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class FindMaxThreatGapTargetStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindMaxThreatGapTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), minThreat(0) {}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
if (!attacker->IsAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (foundHighPriority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IsHighPriority(attacker))
|
||||
{
|
||||
result = attacker;
|
||||
foundHighPriority = true;
|
||||
return;
|
||||
}
|
||||
Unit* victim = attacker->GetVictim();
|
||||
if (!result || CalcThreatGap(attacker, threatMgr) > CalcThreatGap(result, &result->GetThreatMgr()))
|
||||
result = attacker;
|
||||
}
|
||||
float CalcThreatGap(Unit* attacker, ThreatMgr* threatMgr)
|
||||
{
|
||||
Unit* victim = attacker->GetVictim();
|
||||
return threatMgr->GetThreat(victim) - threatMgr->GetThreat(attacker);
|
||||
}
|
||||
|
||||
protected:
|
||||
float minThreat;
|
||||
};
|
||||
|
||||
// caster
|
||||
class CasterFindTargetSmartStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
CasterFindTargetSmartStrategy(PlayerbotAI* botAI, float dps)
|
||||
: FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000)
|
||||
{
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
if (Group* group = botAI->GetBot()->GetGroup())
|
||||
{
|
||||
ObjectGuid guid = group->GetTargetIcon(4);
|
||||
if (guid && attacker->GetGUID() == guid)
|
||||
return;
|
||||
}
|
||||
if (!attacker->IsAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (foundHighPriority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IsHighPriority(attacker))
|
||||
{
|
||||
result = attacker;
|
||||
foundHighPriority = true;
|
||||
return;
|
||||
}
|
||||
float expectedLifeTime = attacker->GetHealth() / dps_;
|
||||
// Unit* victim = attacker->GetVictim();
|
||||
if (!result || IsBetter(attacker, result))
|
||||
{
|
||||
targetExpectedLifeTime = expectedLifeTime;
|
||||
result = attacker;
|
||||
}
|
||||
}
|
||||
bool IsBetter(Unit* new_unit, Unit* old_unit)
|
||||
{
|
||||
float new_time = new_unit->GetHealth() / dps_;
|
||||
float old_time = old_unit->GetHealth() / dps_;
|
||||
// [5-30] > (5-0] > (20-inf)
|
||||
int new_level = GetIntervalLevel(new_unit);
|
||||
int old_level = GetIntervalLevel(old_unit);
|
||||
if (new_level != old_level)
|
||||
{
|
||||
return new_level > old_level;
|
||||
}
|
||||
int32_t level = new_level;
|
||||
if (level % 10 == 2 || level % 10 == 0)
|
||||
{
|
||||
return new_time < old_time;
|
||||
}
|
||||
// dont switch targets when all of them with low health
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||
if (currentTarget == new_unit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (currentTarget == old_unit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return new_time > old_time;
|
||||
}
|
||||
int32_t GetIntervalLevel(Unit* unit)
|
||||
{
|
||||
float time = unit->GetHealth() / dps_;
|
||||
float dis = unit->GetDistance(botAI->GetBot());
|
||||
float attackRange =
|
||||
botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance;
|
||||
attackRange += 5.0f;
|
||||
int level = dis < attackRange ? 10 : 0;
|
||||
if (time >= 5 && time <= 30)
|
||||
{
|
||||
return level + 2;
|
||||
}
|
||||
if (time > 30)
|
||||
{
|
||||
return level;
|
||||
}
|
||||
return level + 1;
|
||||
}
|
||||
|
||||
protected:
|
||||
float dps_;
|
||||
float targetExpectedLifeTime;
|
||||
};
|
||||
|
||||
// General
|
||||
class GeneralFindTargetSmartStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
GeneralFindTargetSmartStrategy(PlayerbotAI* botAI, float dps)
|
||||
: FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000)
|
||||
{
|
||||
}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
if (Group* group = botAI->GetBot()->GetGroup())
|
||||
{
|
||||
ObjectGuid guid = group->GetTargetIcon(4);
|
||||
if (guid && attacker->GetGUID() == guid)
|
||||
return;
|
||||
}
|
||||
if (!attacker->IsAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (foundHighPriority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IsHighPriority(attacker))
|
||||
{
|
||||
result = attacker;
|
||||
foundHighPriority = true;
|
||||
return;
|
||||
}
|
||||
float expectedLifeTime = attacker->GetHealth() / dps_;
|
||||
// Unit* victim = attacker->GetVictim();
|
||||
if (!result || IsBetter(attacker, result))
|
||||
{
|
||||
targetExpectedLifeTime = expectedLifeTime;
|
||||
result = attacker;
|
||||
}
|
||||
}
|
||||
bool IsBetter(Unit* new_unit, Unit* old_unit)
|
||||
{
|
||||
float new_time = new_unit->GetHealth() / dps_;
|
||||
float old_time = old_unit->GetHealth() / dps_;
|
||||
int new_level = GetIntervalLevel(new_unit);
|
||||
int old_level = GetIntervalLevel(old_unit);
|
||||
if (new_level != old_level)
|
||||
{
|
||||
return new_level > old_level;
|
||||
}
|
||||
// attack enemy in range and with lowest health
|
||||
int level = new_level;
|
||||
if (level == 10)
|
||||
{
|
||||
return new_time < old_time;
|
||||
}
|
||||
// all targets are far away, choose the closest one
|
||||
return botAI->GetBot()->GetDistance(new_unit) < botAI->GetBot()->GetDistance(old_unit);
|
||||
}
|
||||
int32_t GetIntervalLevel(Unit* unit)
|
||||
{
|
||||
float time = unit->GetHealth() / dps_;
|
||||
float dis = unit->GetDistance(botAI->GetBot());
|
||||
float attackRange =
|
||||
botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance;
|
||||
attackRange += 5.0f;
|
||||
int level = dis < attackRange ? 10 : 0;
|
||||
return level;
|
||||
}
|
||||
|
||||
protected:
|
||||
float dps_;
|
||||
float targetExpectedLifeTime;
|
||||
};
|
||||
|
||||
// combo
|
||||
class ComboFindTargetSmartStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
ComboFindTargetSmartStrategy(PlayerbotAI* botAI, float dps)
|
||||
: FindTargetStrategy(botAI), dps_(dps), targetExpectedLifeTime(1000000)
|
||||
{
|
||||
}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
if (Group* group = botAI->GetBot()->GetGroup())
|
||||
{
|
||||
ObjectGuid guid = group->GetTargetIcon(4);
|
||||
if (guid && attacker->GetGUID() == guid)
|
||||
return;
|
||||
}
|
||||
if (!attacker->IsAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (foundHighPriority)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IsHighPriority(attacker))
|
||||
{
|
||||
result = attacker;
|
||||
foundHighPriority = true;
|
||||
return;
|
||||
}
|
||||
float expectedLifeTime = attacker->GetHealth() / dps_;
|
||||
// Unit* victim = attacker->GetVictim();
|
||||
if (!result || IsBetter(attacker, result))
|
||||
{
|
||||
targetExpectedLifeTime = expectedLifeTime;
|
||||
result = attacker;
|
||||
}
|
||||
}
|
||||
bool IsBetter(Unit* new_unit, Unit* old_unit)
|
||||
{
|
||||
float new_time = new_unit->GetHealth() / dps_;
|
||||
float old_time = old_unit->GetHealth() / dps_;
|
||||
// [5-20] > (5-0] > (20-inf)
|
||||
int new_level = GetIntervalLevel(new_unit);
|
||||
int old_level = GetIntervalLevel(old_unit);
|
||||
if (new_level != old_level)
|
||||
{
|
||||
return new_level > old_level;
|
||||
}
|
||||
// attack enemy in range and with lowest health
|
||||
int level = new_level;
|
||||
Player* bot = botAI->GetBot();
|
||||
if (level == 10)
|
||||
{
|
||||
Unit* combo_unit = bot->GetComboTarget();
|
||||
if (new_unit == combo_unit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return new_time < old_time;
|
||||
}
|
||||
// all targets are far away, choose the closest one
|
||||
return bot->GetDistance(new_unit) < bot->GetDistance(old_unit);
|
||||
}
|
||||
int32_t GetIntervalLevel(Unit* unit)
|
||||
{
|
||||
float time = unit->GetHealth() / dps_;
|
||||
float dis = unit->GetDistance(botAI->GetBot());
|
||||
float attackRange =
|
||||
botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance;
|
||||
attackRange += 5.0f;
|
||||
int level = dis < attackRange ? 10 : 0;
|
||||
return level;
|
||||
}
|
||||
|
||||
protected:
|
||||
float dps_;
|
||||
float targetExpectedLifeTime;
|
||||
};
|
||||
|
||||
Unit* DpsTargetValue::Calculate()
|
||||
{
|
||||
Unit* rti = RtiTargetValue::Calculate();
|
||||
if (rti)
|
||||
return rti;
|
||||
|
||||
float dps = AI_VALUE(float, "estimated group dps");
|
||||
|
||||
if (botAI->GetNearGroupMemberCount() > 3)
|
||||
{
|
||||
if (botAI->IsCaster(bot))
|
||||
{
|
||||
// Caster find target strategy avoids casting spells on enemies
|
||||
// with too low health to ensure the effectiveness of casting
|
||||
CasterFindTargetSmartStrategy strategy(botAI, dps);
|
||||
return TargetValue::FindTarget(&strategy);
|
||||
}
|
||||
else if (botAI->IsCombo(bot))
|
||||
{
|
||||
ComboFindTargetSmartStrategy strategy(botAI, dps);
|
||||
return TargetValue::FindTarget(&strategy);
|
||||
}
|
||||
}
|
||||
GeneralFindTargetSmartStrategy strategy(botAI, dps);
|
||||
return TargetValue::FindTarget(&strategy);
|
||||
}
|
||||
|
||||
class FindMaxHpTargetStrategy : public FindTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindMaxHpTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), maxHealth(0) {}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
if (Group* group = botAI->GetBot()->GetGroup())
|
||||
{
|
||||
ObjectGuid guid = group->GetTargetIcon(4);
|
||||
if (guid && attacker->GetGUID() == guid)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result || result->GetHealth() < attacker->GetHealth())
|
||||
result = attacker;
|
||||
}
|
||||
|
||||
protected:
|
||||
float maxHealth;
|
||||
};
|
||||
|
||||
Unit* DpsAoeTargetValue::Calculate()
|
||||
{
|
||||
Unit* rti = RtiTargetValue::Calculate();
|
||||
if (rti)
|
||||
return rti;
|
||||
|
||||
FindMaxHpTargetStrategy strategy(botAI);
|
||||
return TargetValue::FindTarget(&strategy);
|
||||
}
|
||||
35
src/Ai/Base/Value/DpsTargetValue.h
Normal file
35
src/Ai/Base/Value/DpsTargetValue.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_DPSTARGETVALUE_H
|
||||
#define _PLAYERBOT_DPSTARGETVALUE_H
|
||||
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class DpsTargetValue : public RtiTargetValue
|
||||
{
|
||||
public:
|
||||
DpsTargetValue(PlayerbotAI* botAI, std::string const type = "rti", std::string const name = "dps target")
|
||||
: RtiTargetValue(botAI, type, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
class DpsAoeTargetValue : public RtiTargetValue
|
||||
{
|
||||
public:
|
||||
DpsAoeTargetValue(PlayerbotAI* botAI, std::string const type = "rti", std::string const name = "dps aoe target")
|
||||
: RtiTargetValue(botAI, type, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
10
src/Ai/Base/Value/DuelTargetValue.cpp
Normal file
10
src/Ai/Base/Value/DuelTargetValue.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 "DuelTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
Unit* DuelTargetValue::Calculate() { return bot->duel ? bot->duel->Opponent : nullptr; }
|
||||
22
src/Ai/Base/Value/DuelTargetValue.h
Normal file
22
src/Ai/Base/Value/DuelTargetValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_DUELTARGETVALUE_H
|
||||
#define _PLAYERBOT_DUELTARGETVALUE_H
|
||||
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class DuelTargetValue : public TargetValue
|
||||
{
|
||||
public:
|
||||
DuelTargetValue(PlayerbotAI* botAI, std::string const name = "duel target") : TargetValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
39
src/Ai/Base/Value/EnemyHealerTargetValue.cpp
Normal file
39
src/Ai/Base/Value/EnemyHealerTargetValue.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 "EnemyHealerTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
Unit* EnemyHealerTargetValue::Calculate()
|
||||
{
|
||||
std::string const spell = qualifier;
|
||||
|
||||
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
|
||||
Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
|
||||
for (ObjectGuid const guid : attackers)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (!unit || unit == target)
|
||||
continue;
|
||||
|
||||
if (sServerFacade->GetDistance2d(bot, unit) > botAI->GetRange("spell"))
|
||||
continue;
|
||||
|
||||
if (!botAI->IsInterruptableSpellCasting(unit, spell))
|
||||
continue;
|
||||
|
||||
Spell* spell = unit->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (spell && spell->m_spellInfo->IsPositive())
|
||||
return unit;
|
||||
|
||||
spell = unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
|
||||
if (spell && spell->m_spellInfo->IsPositive())
|
||||
return unit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
24
src/Ai/Base/Value/EnemyHealerTargetValue.h
Normal file
24
src/Ai/Base/Value/EnemyHealerTargetValue.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ENEMYHEALERTARGETVALUE_H
|
||||
#define _PLAYERBOT_ENEMYHEALERTARGETVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class EnemyHealerTargetValue : public UnitCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
EnemyHealerTargetValue(PlayerbotAI* botAI) : UnitCalculatedValue(botAI, "enemy healer target") {}
|
||||
|
||||
protected:
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
168
src/Ai/Base/Value/EnemyPlayerValue.cpp
Normal file
168
src/Ai/Base/Value/EnemyPlayerValue.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* 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 (sServerFacade->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;
|
||||
}
|
||||
42
src/Ai/Base/Value/EnemyPlayerValue.h
Normal file
42
src/Ai/Base/Value/EnemyPlayerValue.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ENEMYPLAYERVALUE_H
|
||||
#define _PLAYERBOT_ENEMYPLAYERVALUE_H
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PossibleTargetsValue.h"
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class NearestEnemyPlayersValue : public PossibleTargetsValue
|
||||
{
|
||||
public:
|
||||
NearestEnemyPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->grindDistance)
|
||||
: PossibleTargetsValue(botAI, "nearest enemy players", range)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
bool AcceptUnit(Unit* unit) override;
|
||||
};
|
||||
|
||||
class EnemyPlayerValue : public UnitCalculatedValue
|
||||
{
|
||||
public:
|
||||
EnemyPlayerValue(PlayerbotAI* botAI, std::string const name = "enemy player")
|
||||
: UnitCalculatedValue(botAI, name, 1 * 1000)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* Calculate() override;
|
||||
|
||||
private:
|
||||
float GetMaxAttackDistance();
|
||||
};
|
||||
|
||||
#endif
|
||||
149
src/Ai/Base/Value/EstimatedLifetimeValue.cpp
Normal file
149
src/Ai/Base/Value/EstimatedLifetimeValue.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "EstimatedLifetimeValue.h"
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
float EstimatedLifetimeValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
if (!target || !target->IsAlive())
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
float dps = AI_VALUE(float, "estimated group dps");
|
||||
bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3;
|
||||
if (aoePenalty)
|
||||
dps *= 0.75;
|
||||
float res = target->GetHealth() / dps;
|
||||
// bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL);
|
||||
return res;
|
||||
}
|
||||
|
||||
float EstimatedGroupDpsValue::Calculate()
|
||||
{
|
||||
float totalDps = 0;
|
||||
|
||||
std::vector<Player*> groupPlayer = {bot};
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (member == bot) // calculated
|
||||
continue;
|
||||
|
||||
// ignore real player as they may not help with damage
|
||||
if (!GET_PLAYERBOT_AI(member) || GET_PLAYERBOT_AI(member)->IsRealPlayer())
|
||||
continue;
|
||||
|
||||
if (!member || !member->IsInWorld() || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (member->GetMapId() != bot->GetMapId())
|
||||
continue;
|
||||
|
||||
if (member->GetExactDist(bot) > sPlayerbotAIConfig->sightDistance)
|
||||
continue;
|
||||
|
||||
groupPlayer.push_back(member);
|
||||
}
|
||||
}
|
||||
for (Player* player : groupPlayer)
|
||||
{
|
||||
float roleMultiplier;
|
||||
if (botAI->IsTank(player))
|
||||
roleMultiplier = 0.3f;
|
||||
else if (botAI->IsHeal(player))
|
||||
roleMultiplier = 0.1f;
|
||||
else
|
||||
roleMultiplier = 1.0f;
|
||||
float basicDps = GetBasicDps(player->GetLevel());
|
||||
float basicGs = GetBasicGs(player->GetLevel());
|
||||
uint32 mixedGearScore = PlayerbotAI::GetMixedGearScore(player, true, false, 12);
|
||||
float gs_modifier = (float)mixedGearScore / basicGs;
|
||||
// bonus for wotlk epic gear
|
||||
if (mixedGearScore >= 300)
|
||||
{
|
||||
gs_modifier *= 1 + (mixedGearScore - 300) * 0.01;
|
||||
}
|
||||
if (gs_modifier < 0.75)
|
||||
gs_modifier = 0.75;
|
||||
if (gs_modifier > 4)
|
||||
gs_modifier = 4;
|
||||
totalDps += basicDps * roleMultiplier * gs_modifier;
|
||||
}
|
||||
// Group buff bonus
|
||||
if (groupPlayer.size() >= 25)
|
||||
totalDps *= 1.2;
|
||||
else if (groupPlayer.size() >= 10)
|
||||
totalDps *= 1.1;
|
||||
else if (groupPlayer.size() >= 5)
|
||||
totalDps *= 1.05;
|
||||
return totalDps;
|
||||
}
|
||||
|
||||
float EstimatedGroupDpsValue::GetBasicDps(uint32 level)
|
||||
{
|
||||
float basic_dps;
|
||||
|
||||
if (level <= 15)
|
||||
{
|
||||
basic_dps = 5 + level * 1;
|
||||
}
|
||||
else if (level <= 25)
|
||||
{
|
||||
basic_dps = 20 + (level - 15) * 2;
|
||||
}
|
||||
else if (level <= 45)
|
||||
{
|
||||
basic_dps = 40 + (level - 25) * 3;
|
||||
}
|
||||
else if (level <= 55)
|
||||
{
|
||||
basic_dps = 100 + (level - 45) * 20;
|
||||
}
|
||||
else if (level <= 60)
|
||||
{
|
||||
basic_dps = 300 + (level - 55) * 50;
|
||||
}
|
||||
else if (level <= 70)
|
||||
{
|
||||
basic_dps = 550 + (level - 60) * 65;
|
||||
}
|
||||
else
|
||||
{
|
||||
basic_dps = 1200 + (level - 70) * 200;
|
||||
}
|
||||
return basic_dps;
|
||||
}
|
||||
|
||||
float EstimatedGroupDpsValue::GetBasicGs(uint32 level)
|
||||
{
|
||||
float basic_gs;
|
||||
|
||||
if (level <= 8)
|
||||
{
|
||||
basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_NORMAL);
|
||||
}
|
||||
else if (level <= 15)
|
||||
{
|
||||
basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_UNCOMMON);
|
||||
}
|
||||
else if (level <= 60)
|
||||
{
|
||||
basic_gs = PlayerbotFactory::CalcMixedGearScore(level + 5, ITEM_QUALITY_RARE);
|
||||
}
|
||||
else if (level <= 70)
|
||||
{
|
||||
basic_gs = PlayerbotFactory::CalcMixedGearScore(85 + (level - 60) * 3, ITEM_QUALITY_RARE);
|
||||
}
|
||||
else
|
||||
{
|
||||
basic_gs = PlayerbotFactory::CalcMixedGearScore(155 + (level - 70) * 4, ITEM_QUALITY_RARE);
|
||||
}
|
||||
return basic_gs;
|
||||
}
|
||||
40
src/Ai/Base/Value/EstimatedLifetimeValue.h
Normal file
40
src/Ai/Base/Value/EstimatedLifetimeValue.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_EstimatedLifetimeValue_H
|
||||
#define _PLAYERBOT_EstimatedLifetimeValue_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "PossibleTargetsValue.h"
|
||||
#include "TargetValue.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
// [target health] / [expected group single target dps] = [expected lifetime]
|
||||
class EstimatedLifetimeValue : public FloatCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
EstimatedLifetimeValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated lifetime") {}
|
||||
|
||||
public:
|
||||
float Calculate() override;
|
||||
};
|
||||
|
||||
class EstimatedGroupDpsValue : public FloatCalculatedValue
|
||||
{
|
||||
public:
|
||||
EstimatedGroupDpsValue(PlayerbotAI* botAI) : FloatCalculatedValue(botAI, "estimated group dps", 20 * 1000) {}
|
||||
|
||||
public:
|
||||
float Calculate() override;
|
||||
|
||||
protected:
|
||||
float GetBasicDps(uint32 level);
|
||||
float GetBasicGs(uint32 level);
|
||||
};
|
||||
|
||||
#endif
|
||||
55
src/Ai/Base/Value/FishValues.cpp
Normal file
55
src/Ai/Base/Value/FishValues.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 "FishValues.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "Map.h"
|
||||
#include "Spell.h"
|
||||
#include "FishingAction.h"
|
||||
|
||||
bool CanFishValue::Calculate()
|
||||
{
|
||||
int32 SkillFishing = bot->GetSkillValue(SKILL_FISHING);
|
||||
|
||||
if (SkillFishing == 0)
|
||||
return false;
|
||||
|
||||
if (bot->isSwimming())
|
||||
return false;
|
||||
|
||||
if (bot->IsInCombat())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanUseFishingBobberValue::Calculate()
|
||||
{
|
||||
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los");
|
||||
for (auto const& guid : gos)
|
||||
{
|
||||
if (GameObject* go = botAI->GetGameObject(guid))
|
||||
{
|
||||
if (go->GetEntry() != FISHING_BOBBER)
|
||||
continue;
|
||||
if (go->GetOwnerGUID() != bot->GetGUID())
|
||||
continue;
|
||||
|
||||
if (go->getLootState() == GO_READY)
|
||||
return true;
|
||||
|
||||
// Not ready yet → delay next check
|
||||
time_t bobberActiveTime = go->GetRespawnTime() - FISHING_BOBBER_READY_TIME;
|
||||
if (bobberActiveTime > time(0))
|
||||
botAI->SetNextCheckDelay((bobberActiveTime - time(0)) * IN_MILLISECONDS + 500);
|
||||
else
|
||||
botAI->SetNextCheckDelay(1000);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
47
src/Ai/Base/Value/FishValues.h
Normal file
47
src/Ai/Base/Value/FishValues.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_FISHVALUES_H
|
||||
#define _PLAYERBOT_FISHVALUES_H
|
||||
|
||||
#include "Value.h"
|
||||
#include "TravelMgr.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class CanFishValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanFishValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fish") {};
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanUseFishingBobberValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanUseFishingBobberValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can use fishing bobber") {};
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class FishingSpotValue : public ManualSetValue<WorldPosition>
|
||||
{
|
||||
public:
|
||||
FishingSpotValue(PlayerbotAI* botAI, WorldPosition const& pos = WorldPosition(), std::string const& name = "fishing spot")
|
||||
: ManualSetValue<WorldPosition>(botAI, pos, name) {}
|
||||
|
||||
void Set(WorldPosition val) override
|
||||
{
|
||||
value = val;
|
||||
_setTime = getMSTime();
|
||||
}
|
||||
uint32 lastUpdated() const {return _setTime;}
|
||||
bool IsStale(uint32 maxDuration) const { return getMSTime() - _setTime > maxDuration; }
|
||||
|
||||
private:
|
||||
uint32 _setTime = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
702
src/Ai/Base/Value/Formations.cpp
Normal file
702
src/Ai/Base/Value/Formations.cpp
Normal file
@@ -0,0 +1,702 @@
|
||||
/*
|
||||
* 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 "Formations.h"
|
||||
|
||||
#include "Arrow.h"
|
||||
#include "Event.h"
|
||||
#include "Map.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
WorldLocation Formation::NullLocation = WorldLocation();
|
||||
|
||||
bool IsSameLocation(WorldLocation const& a, WorldLocation const& b)
|
||||
{
|
||||
return a.GetPositionX() == b.GetPositionX() && a.GetPositionY() == b.GetPositionY() &&
|
||||
a.GetPositionZ() == b.GetPositionZ() && a.GetMapId() == b.GetMapId();
|
||||
}
|
||||
|
||||
bool Formation::IsNullLocation(WorldLocation const& loc) { return IsSameLocation(loc, Formation::NullLocation); }
|
||||
|
||||
bool ValidateTargetContext(Unit* a, Unit* b, Map*& outMap)
|
||||
{
|
||||
if (!a || !b || a == b)
|
||||
return false;
|
||||
|
||||
if (!a->IsInWorld() || !b->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (a->IsDuringRemoveFromWorld() || b->IsDuringRemoveFromWorld())
|
||||
return false;
|
||||
|
||||
Map* map = a->GetMap();
|
||||
if (!map || map != b->GetMap())
|
||||
return false;
|
||||
|
||||
outMap = map;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateTargetContext(Unit* a, Unit* b)
|
||||
{
|
||||
Map* unused = nullptr;
|
||||
return ValidateTargetContext(a, b, unused);
|
||||
}
|
||||
|
||||
WorldLocation MoveAheadFormation::GetLocation()
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
if (!ValidateTargetContext(master, bot))
|
||||
return Formation::NullLocation;
|
||||
|
||||
WorldLocation loc = GetLocationInternal();
|
||||
if (Formation::IsNullLocation(loc))
|
||||
return loc;
|
||||
|
||||
float x = loc.GetPositionX();
|
||||
float y = loc.GetPositionY();
|
||||
float z = loc.GetPositionZ();
|
||||
|
||||
// if (master->isMoving())
|
||||
// {
|
||||
// float ori = master->GetOrientation();
|
||||
// float x1 = x + sPlayerbotAIConfig->tooCloseDistance * cos(ori);
|
||||
// float y1 = y + sPlayerbotAIConfig->tooCloseDistance * sin(ori);
|
||||
// float ground = map->GetHeight(x1, y1, z);
|
||||
// if (ground > INVALID_HEIGHT)
|
||||
// {
|
||||
// x = x1;
|
||||
// y = y1;
|
||||
// }
|
||||
// }
|
||||
|
||||
// float ground = map->GetHeight(x, y, z);
|
||||
// if (ground <= INVALID_HEIGHT)
|
||||
// return Formation::NullLocation;
|
||||
|
||||
// z += CONTACT_DISTANCE;
|
||||
// bot->UpdateAllowedPositionZ(x, y, z);
|
||||
return WorldLocation(master->GetMapId(), x, y, z);
|
||||
}
|
||||
|
||||
class MeleeFormation : public FollowFormation
|
||||
{
|
||||
public:
|
||||
MeleeFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "melee") {}
|
||||
|
||||
std::string const GetTargetName() override { return "group leader"; }
|
||||
};
|
||||
|
||||
class QueueFormation : public FollowFormation
|
||||
{
|
||||
public:
|
||||
QueueFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "queue") {}
|
||||
|
||||
std::string const GetTargetName() override { return "line target"; }
|
||||
};
|
||||
|
||||
class NearFormation : public MoveAheadFormation
|
||||
{
|
||||
public:
|
||||
NearFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "near") {}
|
||||
|
||||
WorldLocation GetLocationInternal() override
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
Map* map = nullptr;
|
||||
if (!ValidateTargetContext(master, bot, map))
|
||||
return Formation::NullLocation;
|
||||
|
||||
float range = sPlayerbotAIConfig->followDistance;
|
||||
float angle = GetFollowAngle();
|
||||
float x = master->GetPositionX() + cos(angle) * range;
|
||||
float y = master->GetPositionY() + sin(angle) * range;
|
||||
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
if (!map->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||
master->GetPositionZ(), x, y, z))
|
||||
{
|
||||
x = master->GetPositionX() + cos(angle) * range;
|
||||
y = master->GetPositionY() + sin(angle) * range;
|
||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
master->UpdateAllowedPositionZ(x, y, z);
|
||||
}
|
||||
return WorldLocation(master->GetMapId(), x, y, z);
|
||||
}
|
||||
|
||||
float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance; }
|
||||
};
|
||||
|
||||
class ChaosFormation : public MoveAheadFormation
|
||||
{
|
||||
public:
|
||||
ChaosFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "chaos"), lastChangeTime(0) {}
|
||||
|
||||
WorldLocation GetLocationInternal() override
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
Map* map = nullptr;
|
||||
if (!ValidateTargetContext(master, bot, map))
|
||||
return Formation::NullLocation;
|
||||
|
||||
float range = sPlayerbotAIConfig->followDistance;
|
||||
float angle = GetFollowAngle();
|
||||
|
||||
time_t now = time(nullptr);
|
||||
if (!lastChangeTime || now - lastChangeTime >= 3)
|
||||
{
|
||||
lastChangeTime = now;
|
||||
|
||||
dx = (urand(0, 10) / 10.0f - 0.5f) * sPlayerbotAIConfig->tooCloseDistance;
|
||||
dy = (urand(0, 10) / 10.0f - 0.5f) * sPlayerbotAIConfig->tooCloseDistance;
|
||||
dr = std::sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
float x = master->GetPositionX() + std::cos(angle) * range + dx;
|
||||
float y = master->GetPositionY() + std::sin(angle) * range + dy;
|
||||
float z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
|
||||
if (!map->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||
master->GetPositionZ(), x, y, z))
|
||||
{
|
||||
// Recompute a clean fallback and clamp Z
|
||||
x = master->GetPositionX() + std::cos(angle) * range + dx;
|
||||
y = master->GetPositionY() + std::sin(angle) * range + dy;
|
||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
|
||||
master->UpdateAllowedPositionZ(x, y, z);
|
||||
}
|
||||
|
||||
return WorldLocation(master->GetMapId(), x, y, z);
|
||||
}
|
||||
|
||||
float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance + dr; }
|
||||
|
||||
private:
|
||||
time_t lastChangeTime;
|
||||
float dx = 0.f;
|
||||
float dy = 0.f;
|
||||
float dr = 0.f;
|
||||
};
|
||||
|
||||
class CircleFormation : public MoveFormation
|
||||
{
|
||||
public:
|
||||
CircleFormation(PlayerbotAI* botAI) : MoveFormation(botAI, "circle") {}
|
||||
|
||||
WorldLocation GetLocation() override
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
Player* master = GetMaster();
|
||||
|
||||
// Fix: if no target OR target is the bot, fall back to master
|
||||
if (!target || target == bot)
|
||||
target = master;
|
||||
|
||||
Map* map = nullptr;
|
||||
if (!ValidateTargetContext(master, bot, map))
|
||||
return Formation::NullLocation;
|
||||
|
||||
float range = 2.0f;
|
||||
switch (bot->getClass())
|
||||
{
|
||||
case CLASS_HUNTER:
|
||||
case CLASS_MAGE:
|
||||
case CLASS_PRIEST:
|
||||
case CLASS_WARLOCK:
|
||||
range = botAI->GetRange("flee");
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (!botAI->IsTank(bot))
|
||||
range = botAI->GetRange("flee");
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
if (botAI->IsHeal(bot))
|
||||
range = botAI->GetRange("flee");
|
||||
break;
|
||||
}
|
||||
|
||||
float angle = GetFollowAngle();
|
||||
float x = target->GetPositionX() + cos(angle) * range;
|
||||
float y = target->GetPositionY() + sin(angle) * range;
|
||||
float z = target->GetPositionZ();
|
||||
|
||||
if (!map->CheckCollisionAndGetValidCoords(target, target->GetPositionX(), target->GetPositionY(),
|
||||
target->GetPositionZ(), x, y, z))
|
||||
{
|
||||
x = target->GetPositionX() + cos(angle) * range;
|
||||
y = target->GetPositionY() + sin(angle) * range;
|
||||
z = target->GetPositionZ();
|
||||
target->UpdateAllowedPositionZ(x, y, z);
|
||||
}
|
||||
|
||||
return WorldLocation(bot->GetMapId(), x, y, z);
|
||||
}
|
||||
};
|
||||
|
||||
class LineFormation : public MoveAheadFormation
|
||||
{
|
||||
public:
|
||||
LineFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "line") {}
|
||||
|
||||
WorldLocation GetLocationInternal() override
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float range = 2.0f;
|
||||
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float x = master->GetPositionX();
|
||||
float y = master->GetPositionY();
|
||||
float z = master->GetPositionZ();
|
||||
float orientation = master->GetOrientation();
|
||||
|
||||
std::vector<Player*> players;
|
||||
players.reserve(group->GetMembersCount());
|
||||
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member || member == master)
|
||||
continue;
|
||||
|
||||
players.push_back(member);
|
||||
}
|
||||
|
||||
players.insert(players.begin() + players.size() / 2, master);
|
||||
return MoveLine(players, 0.0f, x, y, z, orientation, range);
|
||||
}
|
||||
};
|
||||
|
||||
class ShieldFormation : public MoveFormation
|
||||
{
|
||||
public:
|
||||
ShieldFormation(PlayerbotAI* botAI) : MoveFormation(botAI, "shield") {}
|
||||
|
||||
WorldLocation GetLocation() override
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float range = sPlayerbotAIConfig->followDistance;
|
||||
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float x = master->GetPositionX();
|
||||
float y = master->GetPositionY();
|
||||
float z = master->GetPositionZ();
|
||||
float orientation = master->GetOrientation();
|
||||
|
||||
std::vector<Player*> tanks;
|
||||
std::vector<Player*> dps;
|
||||
|
||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||
{
|
||||
Player* member = gref->GetSource();
|
||||
if (!member || member == master)
|
||||
continue;
|
||||
|
||||
if (botAI->IsTank(member))
|
||||
tanks.push_back(member);
|
||||
else
|
||||
dps.push_back(member);
|
||||
}
|
||||
|
||||
if (botAI->IsTank(master))
|
||||
tanks.insert(tanks.begin() + (tanks.size() + 1) / 2, master);
|
||||
else
|
||||
dps.insert(dps.begin() + (dps.size() + 1) / 2, master);
|
||||
|
||||
if (botAI->IsTank(bot) && botAI->IsTank(master))
|
||||
return MoveLine(tanks, 0.0f, x, y, z, orientation, range);
|
||||
|
||||
if (!botAI->IsTank(bot) && !botAI->IsTank(master))
|
||||
return MoveLine(dps, 0.0f, x, y, z, orientation, range);
|
||||
|
||||
if (botAI->IsTank(bot) && !botAI->IsTank(master))
|
||||
{
|
||||
float diff = (tanks.size() % 2 == 0) ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f;
|
||||
return MoveLine(tanks, diff, x + cos(orientation) * range, y + sin(orientation) * range, z, orientation,
|
||||
range);
|
||||
}
|
||||
|
||||
if (!botAI->IsTank(bot) && botAI->IsTank(master))
|
||||
{
|
||||
float diff = (dps.size() % 2 == 0) ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f;
|
||||
return MoveLine(dps, diff, x - cos(orientation) * range, y - sin(orientation) * range, z, orientation,
|
||||
range);
|
||||
}
|
||||
|
||||
return Formation::NullLocation;
|
||||
}
|
||||
};
|
||||
|
||||
class FarFormation : public FollowFormation
|
||||
{
|
||||
public:
|
||||
FarFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "far") {}
|
||||
|
||||
WorldLocation GetLocation() override
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
Map* map = nullptr;
|
||||
if (!ValidateTargetContext(master, bot, map))
|
||||
return Formation::NullLocation;
|
||||
|
||||
float range = sPlayerbotAIConfig->farDistance;
|
||||
float followRange = sPlayerbotAIConfig->followDistance;
|
||||
|
||||
if (sServerFacade->GetDistance2d(bot, master) <= range)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float angleToBot = master->GetAngle(bot);
|
||||
float followAngle = GetFollowAngle();
|
||||
|
||||
float x = master->GetPositionX() + cos(angleToBot) * range + cos(followAngle) * followRange;
|
||||
float y = master->GetPositionY() + sin(angleToBot) * range + sin(followAngle) * followRange;
|
||||
float z = master->GetPositionZ();
|
||||
|
||||
float ground = master->GetMapHeight(x, y, z + 30.0f);
|
||||
if (ground <= INVALID_HEIGHT)
|
||||
{
|
||||
float minDist = 0.f;
|
||||
float minX = 0.f, minY = 0.f;
|
||||
|
||||
for (float a = 0.0f; a <= 2 * M_PI; a += M_PI / 16.0f)
|
||||
{
|
||||
float tx = master->GetPositionX() + cos(a) * range + cos(followAngle) * followRange;
|
||||
float ty = master->GetPositionY() + sin(a) * range + sin(followAngle) * followRange;
|
||||
|
||||
float dist = sServerFacade->GetDistance2d(bot, tx, ty);
|
||||
float tg = master->GetMapHeight(tx, ty, z + 30.0f);
|
||||
|
||||
if (tg > INVALID_HEIGHT && (!minDist || dist < minDist))
|
||||
{
|
||||
minDist = dist;
|
||||
minX = tx;
|
||||
minY = ty;
|
||||
}
|
||||
}
|
||||
|
||||
if (!minDist)
|
||||
return Formation::NullLocation;
|
||||
|
||||
float lz = z;
|
||||
if (!map->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||
master->GetPositionZ(), minX, minY, lz))
|
||||
{
|
||||
lz = z + master->GetHoverHeight();
|
||||
master->UpdateAllowedPositionZ(minX, minY, lz);
|
||||
}
|
||||
|
||||
return WorldLocation(bot->GetMapId(), minX, minY, lz);
|
||||
}
|
||||
|
||||
if (!map->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||
master->GetPositionZ(), x, y, z))
|
||||
{
|
||||
x = master->GetPositionX() + cos(angleToBot) * range + cos(followAngle) * followRange;
|
||||
y = master->GetPositionY() + sin(angleToBot) * range + sin(followAngle) * followRange;
|
||||
z = master->GetPositionZ() + master->GetHoverHeight();
|
||||
master->UpdateAllowedPositionZ(x, y, z);
|
||||
}
|
||||
|
||||
return WorldLocation(bot->GetMapId(), x, y, z);
|
||||
}
|
||||
};
|
||||
|
||||
float Formation::GetFollowAngle()
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
Group* group = bot->GetGroup();
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||
|
||||
// If there's no master and no group
|
||||
if (!master && !group)
|
||||
return 0.0f;
|
||||
|
||||
uint32 index = 1;
|
||||
uint32 total = 1;
|
||||
|
||||
std::vector<Player*> roster;
|
||||
|
||||
if (group)
|
||||
{
|
||||
bool left = true; // Used for alternating tanks' positions
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
|
||||
// Skip invalid, dead, or out-of-map members
|
||||
if (!member || !member->IsAlive() || bot->GetMapId() != member->GetMapId())
|
||||
continue;
|
||||
|
||||
// Skip the master
|
||||
if (member == master)
|
||||
continue;
|
||||
|
||||
// Put DPS in the middle
|
||||
if (!botAI->IsTank(member) && !botAI->IsHeal(member))
|
||||
{
|
||||
roster.insert(roster.begin() + roster.size() / 2, member);
|
||||
}
|
||||
|
||||
// Put Healers in the middle
|
||||
else if (botAI->IsHeal(member))
|
||||
{
|
||||
roster.insert(roster.begin() + roster.size() / 2, member);
|
||||
}
|
||||
|
||||
// Handle tanks (alternate between front and back)
|
||||
else if (botAI->IsTank(member))
|
||||
{
|
||||
if (left)
|
||||
roster.push_back(member); // Place tank at the back
|
||||
else
|
||||
roster.insert(roster.begin(), member); // Place tank at the front
|
||||
|
||||
left = !left; // Alternate for the next tank
|
||||
}
|
||||
|
||||
total++;
|
||||
}
|
||||
}
|
||||
else if (master)
|
||||
{
|
||||
// If the bot is following a master, look up the bot's position in the master's list
|
||||
PlayerbotMgr* masterBotMgr = GET_PLAYERBOT_MGR(master);
|
||||
if (masterBotMgr && !GET_PLAYERBOT_AI(master))
|
||||
{
|
||||
for (auto it = masterBotMgr->GetPlayerBotsBegin(); it != masterBotMgr->GetPlayerBotsEnd(); ++it)
|
||||
{
|
||||
if (it->second == bot)
|
||||
{
|
||||
index = total; // Found bot in master's list, set the index
|
||||
break;
|
||||
}
|
||||
++total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the bot's position in the roster
|
||||
auto it = std::find(roster.begin(), roster.end(), bot);
|
||||
if (it != roster.end())
|
||||
{
|
||||
index = std::distance(roster.begin(), it) + 1; // Find bot's index in the roster
|
||||
}
|
||||
|
||||
// Return
|
||||
float start = (master ? master->GetOrientation() : 0.0f);
|
||||
return start + (0.125f + 1.75f * index / total + (total == 2 ? 0.125f : 0.0f)) * M_PI;
|
||||
}
|
||||
|
||||
FormationValue::FormationValue(PlayerbotAI* botAI)
|
||||
: ManualSetValue<Formation*>(botAI, new ChaosFormation(botAI), "formation")
|
||||
{
|
||||
}
|
||||
|
||||
FormationValue::~FormationValue()
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
delete value;
|
||||
value = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string const FormationValue::Save() { return value ? value->getName() : "?"; }
|
||||
|
||||
bool FormationValue::Load(std::string const formation)
|
||||
{
|
||||
if (formation == "melee")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new MeleeFormation(botAI);
|
||||
}
|
||||
else if (formation == "queue")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new QueueFormation(botAI);
|
||||
}
|
||||
else if (formation == "chaos" || formation == "default")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new ChaosFormation(botAI);
|
||||
}
|
||||
else if (formation == "circle")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new CircleFormation(botAI);
|
||||
}
|
||||
else if (formation == "line")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new LineFormation(botAI);
|
||||
}
|
||||
else if (formation == "shield")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new ShieldFormation(botAI);
|
||||
}
|
||||
else if (formation == "arrow")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new ArrowFormation(botAI);
|
||||
}
|
||||
else if (formation == "near")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new NearFormation(botAI);
|
||||
}
|
||||
else if (formation == "far")
|
||||
{
|
||||
if (value)
|
||||
delete value;
|
||||
|
||||
value = new FarFormation(botAI);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetFormationAction::Execute(Event event)
|
||||
{
|
||||
std::string const formation = event.getParam();
|
||||
|
||||
FormationValue* value = (FormationValue*)context->GetValue<Formation*>("formation");
|
||||
if (formation == "?" || formation.empty())
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << "Formation: |cff00ff00" << value->Get()->getName();
|
||||
botAI->TellMaster(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (formation == "show")
|
||||
{
|
||||
WorldLocation loc = value->Get()->GetLocation();
|
||||
if (!Formation::IsNullLocation(loc))
|
||||
botAI->Ping(loc.GetPositionX(), loc.GetPositionY());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!value->Load(formation))
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << "Invalid formation: |cffff0000" << formation;
|
||||
botAI->TellMaster(str);
|
||||
botAI->TellMaster(
|
||||
"Please set to any of:|cffffffff chaos (default), near, queue, circle, line, shield, arrow, melee, far");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ostringstream str;
|
||||
str << "Formation set to: " << formation;
|
||||
botAI->TellMaster(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
WorldLocation MoveFormation::MoveLine(std::vector<Player*> line, float diff, float cx, float cy, float cz,
|
||||
float orientation, float range)
|
||||
{
|
||||
if (line.size() < 5)
|
||||
{
|
||||
return MoveSingleLine(line, diff, cx, cy, cz, orientation, range);
|
||||
}
|
||||
|
||||
uint32 lines = ceil((double)line.size() / 5.0);
|
||||
for (uint32 i = 0; i < lines; i++)
|
||||
{
|
||||
float radius = range * i;
|
||||
float x = cx + cos(orientation) * radius;
|
||||
float y = cy + sin(orientation) * radius;
|
||||
|
||||
std::vector<Player*> singleLine;
|
||||
for (uint32 j = 0; j < 5 && !line.empty(); j++)
|
||||
{
|
||||
singleLine.push_back(line[line.size() - 1]);
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
WorldLocation loc = MoveSingleLine(singleLine, diff, x, y, cz, orientation, range);
|
||||
if (!Formation::IsNullLocation(loc))
|
||||
return loc;
|
||||
}
|
||||
|
||||
return Formation::NullLocation;
|
||||
}
|
||||
|
||||
WorldLocation MoveFormation::MoveSingleLine(std::vector<Player*> line, float diff, float cx, float cy, float cz,
|
||||
float orientation, float range)
|
||||
{
|
||||
float count = line.size();
|
||||
float angleLeft = orientation - M_PI / 2.0f;
|
||||
float x0 = cx + std::cos(angleLeft) * (range * std::floor(count / 2.0f) + diff);
|
||||
float y0 = cy + std::sin(angleLeft) * (range * std::floor(count / 2.0f) + diff);
|
||||
|
||||
uint32 index = 0;
|
||||
for (Player* member : line)
|
||||
{
|
||||
if (member == bot)
|
||||
{
|
||||
float angleRight = orientation + M_PI / 2.0f;
|
||||
float radius = range * index;
|
||||
|
||||
float lx = x0 + std::cos(angleRight) * radius;
|
||||
float ly = y0 + std::sin(angleRight) * radius;
|
||||
float lz = cz;
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
Map* map = master ? master->GetMap() : nullptr;
|
||||
|
||||
// if not fully in world ignore collision corrections.
|
||||
if (!master || !map || !bot || map != bot->GetMap() || !master->IsInWorld() ||
|
||||
master->IsDuringRemoveFromWorld() || !bot->IsInWorld() || bot->IsDuringRemoveFromWorld())
|
||||
{
|
||||
return WorldLocation(bot->GetMapId(), lx, ly, lz);
|
||||
}
|
||||
|
||||
// if fully loaded check collision and applies coordinate corrections if needed
|
||||
map->CheckCollisionAndGetValidCoords(master, master->GetPositionX(), master->GetPositionY(),
|
||||
master->GetPositionZ(), lx, ly, lz);
|
||||
|
||||
return WorldLocation(bot->GetMapId(), lx, ly, lz);
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return Formation::NullLocation;
|
||||
}
|
||||
77
src/Ai/Base/Value/Formations.h
Normal file
77
src/Ai/Base/Value/Formations.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_FORMATIONS_H
|
||||
#define _PLAYERBOT_FORMATIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "TravelMgr.h"
|
||||
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
|
||||
class Formation : public AiNamedObject
|
||||
{
|
||||
public:
|
||||
Formation(PlayerbotAI* botAI, std::string const name) : AiNamedObject(botAI, name) {}
|
||||
virtual ~Formation() = default;
|
||||
virtual std::string const GetTargetName() { return ""; }
|
||||
virtual WorldLocation GetLocation() { return NullLocation; }
|
||||
virtual float GetMaxDistance() { return sPlayerbotAIConfig->followDistance; }
|
||||
static WorldLocation NullLocation;
|
||||
static bool IsNullLocation(WorldLocation const& loc);
|
||||
|
||||
protected:
|
||||
float GetFollowAngle();
|
||||
};
|
||||
|
||||
class FollowFormation : public Formation
|
||||
{
|
||||
public:
|
||||
FollowFormation(PlayerbotAI* botAI, std::string const name) : Formation(botAI, name) {}
|
||||
};
|
||||
|
||||
class MoveFormation : public Formation
|
||||
{
|
||||
public:
|
||||
MoveFormation(PlayerbotAI* botAI, std::string const name) : Formation(botAI, name) {}
|
||||
|
||||
protected:
|
||||
WorldLocation MoveLine(std::vector<Player*> line, float diff, float cx, float cy, float cz, float orientation,
|
||||
float range);
|
||||
WorldLocation MoveSingleLine(std::vector<Player*> line, float diff, float cx, float cy, float cz, float orientation,
|
||||
float range);
|
||||
};
|
||||
|
||||
class MoveAheadFormation : public MoveFormation
|
||||
{
|
||||
public:
|
||||
MoveAheadFormation(PlayerbotAI* botAI, std::string const name) : MoveFormation(botAI, name) {}
|
||||
|
||||
WorldLocation GetLocation() override;
|
||||
virtual WorldLocation GetLocationInternal() { return NullLocation; }
|
||||
};
|
||||
|
||||
class FormationValue : public ManualSetValue<Formation*>
|
||||
{
|
||||
public:
|
||||
FormationValue(PlayerbotAI* botAI);
|
||||
~FormationValue();
|
||||
|
||||
std::string const Save() override;
|
||||
bool Load(std::string const value) override;
|
||||
};
|
||||
|
||||
class SetFormationAction : public Action
|
||||
{
|
||||
public:
|
||||
SetFormationAction(PlayerbotAI* botAI) : Action(botAI, "set formation") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
233
src/Ai/Base/Value/GrindTargetValue.cpp
Normal file
233
src/Ai/Base/Value/GrindTargetValue.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* 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 "GrindTargetValue.h"
|
||||
|
||||
#include "NewRpgInfo.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ReputationMgr.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "SharedDefines.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_WANDER_RANDOM && botAI->rpgInfo.status != RPG_IDLE;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
27
src/Ai/Base/Value/GrindTargetValue.h
Normal file
27
src/Ai/Base/Value/GrindTargetValue.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_GRINDTARGETVALUE_H
|
||||
#define _PLAYERBOT_GRINDTARGETVALUE_H
|
||||
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class GrindTargetValue : public TargetValue
|
||||
{
|
||||
public:
|
||||
GrindTargetValue(PlayerbotAI* botAI, std::string const name = "grind target") : TargetValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
|
||||
private:
|
||||
uint32 GetTargetingPlayerCount(Unit* unit);
|
||||
Unit* FindTargetForGrinding(uint32 assistCount);
|
||||
bool needForQuest(Unit* target);
|
||||
};
|
||||
|
||||
#endif
|
||||
10
src/Ai/Base/Value/GroupLeaderValue.cpp
Normal file
10
src/Ai/Base/Value/GroupLeaderValue.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 "GroupLeaderValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
Unit* GroupLeaderValue::Calculate() { return botAI->GetGroupLeader(); }
|
||||
24
src/Ai/Base/Value/GroupLeaderValue.h
Normal file
24
src/Ai/Base/Value/GroupLeaderValue.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_GROUPLEADERVALUE_H
|
||||
#define _PLAYERBOT_GROUPLEADERVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class GroupLeaderValue : public UnitCalculatedValue
|
||||
{
|
||||
public:
|
||||
GroupLeaderValue(PlayerbotAI* botAI, std::string const name = "group leader") : UnitCalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
174
src/Ai/Base/Value/GroupValues.cpp
Normal file
174
src/Ai/Base/Value/GroupValues.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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 "GroupValues.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "ServerFacade.h"
|
||||
|
||||
GuidVector GroupMembersValue::Calculate()
|
||||
{
|
||||
GuidVector members;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
members.push_back(ref->GetSource()->GetGUID());
|
||||
}
|
||||
}
|
||||
else
|
||||
members.push_back(bot->GetGUID());
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
bool IsFollowingPartyValue::Calculate()
|
||||
{
|
||||
if (botAI->GetGroupLeader() == bot)
|
||||
return true;
|
||||
|
||||
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
bool IsNearLeaderValue::Calculate()
|
||||
{
|
||||
Player* groupLeader = botAI->GetGroupLeader();
|
||||
|
||||
if (!groupLeader)
|
||||
return false;
|
||||
|
||||
if (groupLeader == bot)
|
||||
return true;
|
||||
|
||||
return sServerFacade->GetDistance2d(bot, botAI->GetGroupLeader()) < sPlayerbotAIConfig->sightDistance;
|
||||
}
|
||||
|
||||
bool BoolANDValue::Calculate()
|
||||
{
|
||||
std::vector<std::string> values = split(getQualifier(), ',');
|
||||
|
||||
for (auto value : values)
|
||||
{
|
||||
if (!AI_VALUE(bool, value))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 GroupBoolCountValue::Calculate()
|
||||
{
|
||||
uint32 count = 0;
|
||||
|
||||
for (ObjectGuid guid : AI_VALUE(GuidVector, "group members"))
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
|
||||
if (!player)
|
||||
continue;
|
||||
|
||||
if (player->GetMapId() != bot->GetMapId())
|
||||
continue;
|
||||
|
||||
if (!GET_PLAYERBOT_AI(player))
|
||||
continue;
|
||||
|
||||
if (PAI_VALUE2(bool, "and", getQualifier()))
|
||||
return count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
bool GroupBoolANDValue::Calculate()
|
||||
{
|
||||
for (ObjectGuid guid : AI_VALUE(GuidVector, "group members"))
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
|
||||
if (!player)
|
||||
continue;
|
||||
|
||||
if (player->GetMapId() != bot->GetMapId())
|
||||
continue;
|
||||
|
||||
if (!GET_PLAYERBOT_AI(player))
|
||||
continue;
|
||||
|
||||
if (!PAI_VALUE2(bool, "and", getQualifier()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool GroupBoolORValue::Calculate()
|
||||
{
|
||||
for (ObjectGuid guid : AI_VALUE(GuidVector, "group members"))
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(guid);
|
||||
|
||||
if (!player)
|
||||
continue;
|
||||
|
||||
if (player->GetMapId() != bot->GetMapId())
|
||||
continue;
|
||||
|
||||
if (!GET_PLAYERBOT_AI(player))
|
||||
continue;
|
||||
|
||||
if (PAI_VALUE2(bool, "and", getQualifier()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
bool GroupReadyValue::Calculate()
|
||||
{
|
||||
bool inDungeon = !WorldPosition(bot).isOverworld();
|
||||
|
||||
for (ObjectGuid guid : AI_VALUE(GuidVector, "group members"))
|
||||
{
|
||||
Player* member = ObjectAccessor::FindPlayer(guid);
|
||||
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
if (inDungeon) // In dungeons all following members need to be alive before continueing.
|
||||
{
|
||||
PlayerbotAI* memberAi = GET_PLAYERBOT_AI(member);
|
||||
|
||||
bool isFollowing = memberAi ? memberAi->HasStrategy("follow", BOT_STATE_NON_COMBAT) : true;
|
||||
|
||||
if (!member->IsAlive() && isFollowing)
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only wait for members that are in range otherwise we might be waiting for bots stuck in dead loops
|
||||
// forever.
|
||||
if (botAI->GetGroupLeader() &&
|
||||
sServerFacade->GetDistance2d(member, botAI->GetGroupLeader()) > sPlayerbotAIConfig->sightDistance)
|
||||
continue;
|
||||
|
||||
if (member->GetHealthPct() < sPlayerbotAIConfig->almostFullHealth)
|
||||
return false;
|
||||
|
||||
if (!member->GetPower(POWER_MANA))
|
||||
continue;
|
||||
|
||||
float mana = (static_cast<float>(member->GetPower(POWER_MANA)) / member->GetMaxPower(POWER_MANA)) * 100;
|
||||
|
||||
if (mana < sPlayerbotAIConfig->mediumMana)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
73
src/Ai/Base/Value/GroupValues.h
Normal file
73
src/Ai/Base/Value/GroupValues.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class GroupMembersValue : public ObjectGuidListCalculatedValue
|
||||
{
|
||||
public:
|
||||
GroupMembersValue(PlayerbotAI* botAI) : ObjectGuidListCalculatedValue(botAI, "group members", 2 * 1000) {}
|
||||
|
||||
GuidVector Calculate() override;
|
||||
};
|
||||
|
||||
class IsFollowingPartyValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
IsFollowingPartyValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "following party") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class IsNearLeaderValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
IsNearLeaderValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "near leader") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class BoolANDValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
BoolANDValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "bool and") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class GroupBoolCountValue : public Uint32CalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
GroupBoolCountValue(PlayerbotAI* botAI) : Uint32CalculatedValue(botAI, "group count") {}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class GroupBoolANDValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
GroupBoolANDValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "group bool and") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class GroupBoolORValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
GroupBoolORValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "group bool or") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class GroupReadyValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
GroupReadyValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "group ready", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
22
src/Ai/Base/Value/GuildValues.cpp
Normal file
22
src/Ai/Base/Value/GuildValues.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 "GuildValues.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
uint8 PetitionSignsValue::Calculate()
|
||||
{
|
||||
if (bot->GetGuildId())
|
||||
return 0;
|
||||
|
||||
std::vector<Item*> petitions = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatQItem(5863));
|
||||
if (petitions.empty())
|
||||
return 0;
|
||||
|
||||
QueryResult result = CharacterDatabase.Query("SELECT playerguid FROM petition_sign WHERE petitionguid = {}",
|
||||
petitions.front()->GetGUID().GetCounter());
|
||||
return result ? (uint8)result->GetRowCount() : 0;
|
||||
}
|
||||
21
src/Ai/Base/Value/GuildValues.h
Normal file
21
src/Ai/Base/Value/GuildValues.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_GUILDVALUES_H
|
||||
#define _PLAYERBOT_GUILDVALUES_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class PetitionSignsValue : public SingleCalculatedValue<uint8>
|
||||
{
|
||||
public:
|
||||
PetitionSignsValue(PlayerbotAI* botAI) : SingleCalculatedValue<uint8>(botAI, "petition signs") {}
|
||||
|
||||
uint8 Calculate();
|
||||
};
|
||||
|
||||
#endif
|
||||
15
src/Ai/Base/Value/HasAvailableLootValue.cpp
Normal file
15
src/Ai/Base/Value/HasAvailableLootValue.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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 "HasAvailableLootValue.h"
|
||||
|
||||
#include "LootObjectStack.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool HasAvailableLootValue::Calculate()
|
||||
{
|
||||
return !AI_VALUE(bool, "can loot") &&
|
||||
AI_VALUE(LootObjectStack*, "available loot")->CanLoot(sPlayerbotAIConfig->lootDistance);
|
||||
}
|
||||
21
src/Ai/Base/Value/HasAvailableLootValue.h
Normal file
21
src/Ai/Base/Value/HasAvailableLootValue.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_HASAVAILABLELOOTVALUE_H
|
||||
#define _PLAYERBOT_HASAVAILABLELOOTVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class HasAvailableLootValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
HasAvailableLootValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
70
src/Ai/Base/Value/HasTotemValue.cpp
Normal file
70
src/Ai/Base/Value/HasTotemValue.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 "HasTotemValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
char* strstri(char const* str1, char const* str2);
|
||||
|
||||
bool HasTotemValue::Calculate()
|
||||
{
|
||||
for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
|
||||
{
|
||||
if (!bot->m_SummonSlot[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Creature* OldTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[i]))
|
||||
{
|
||||
if (OldTotem->IsSummon() && OldTotem->GetDistance(bot) <= 30.0f)
|
||||
{
|
||||
if (strstri(OldTotem->GetName().c_str(), qualifier.c_str()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// bool HasTotemValue::Calculate()
|
||||
// {
|
||||
// for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
|
||||
// {
|
||||
// if (!bot->m_SummonSlot[i])
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if (Creature* OldTotem = bot->GetMap()->GetCreature(bot->m_SummonSlot[i]))
|
||||
// {
|
||||
// if (OldTotem->IsSummon())
|
||||
// {
|
||||
// if (strstri(creature->GetName().c_str(), qualifier.c_str()))
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// GuidVector units = *context->GetValue<GuidVector>("nearest totems");
|
||||
// for (ObjectGuid const guid : units)
|
||||
// {
|
||||
// Unit* unit = botAI->GetUnit(guid);
|
||||
// if (!unit)
|
||||
// continue;
|
||||
// Creature* creature = dynamic_cast<Creature*>(unit);
|
||||
|
||||
// if (creature->GetOwner() != bot)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// if (strstri(creature->GetName().c_str(), qualifier.c_str()))
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// }
|
||||
22
src/Ai/Base/Value/HasTotemValue.h
Normal file
22
src/Ai/Base/Value/HasTotemValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_HASTOTEMVALUE_H
|
||||
#define _PLAYERBOT_HASTOTEMVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class HasTotemValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
HasTotemValue(PlayerbotAI* botAI, std::string const name = "has totem") : BoolCalculatedValue(botAI, name) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
29
src/Ai/Base/Value/InvalidTargetValue.cpp
Normal file
29
src/Ai/Base/Value/InvalidTargetValue.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 "InvalidTargetValue.h"
|
||||
|
||||
#include "AttackersValue.h"
|
||||
#include "Playerbots.h"
|
||||
#include "Unit.h"
|
||||
|
||||
bool InvalidTargetValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
Unit* enemy = AI_VALUE(Unit*, "enemy player target");
|
||||
if (target && enemy && target == enemy && target->IsAlive())
|
||||
return false;
|
||||
|
||||
if (target && qualifier == "current target")
|
||||
{
|
||||
return target->GetMapId() != bot->GetMapId() || target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) ||
|
||||
target->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) || target->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE_2) ||
|
||||
!target->IsVisible() || !target->IsAlive() || target->IsPolymorphed() || target->IsCharmed() ||
|
||||
target->HasFearAura() || target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsFriendlyTo(bot) ||
|
||||
!AttackersValue::IsValidTarget(target, bot);
|
||||
}
|
||||
|
||||
return !target;
|
||||
}
|
||||
24
src/Ai/Base/Value/InvalidTargetValue.h
Normal file
24
src/Ai/Base/Value/InvalidTargetValue.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_INVALIDTARGETVALUE_H
|
||||
#define _PLAYERBOT_INVALIDTARGETVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class InvalidTargetValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
InvalidTargetValue(PlayerbotAI* botAI, std::string const name = "invalid target") : BoolCalculatedValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
24
src/Ai/Base/Value/IsBehindValue.cpp
Normal file
24
src/Ai/Base/Value/IsBehindValue.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 "IsBehindValue.h"
|
||||
#include <cmath>
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool IsBehindValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
float targetOrientation = target->GetOrientation();
|
||||
|
||||
float deltaAngle = Position::NormalizeOrientation(targetOrientation - target->GetAngle(bot));
|
||||
if (deltaAngle > M_PI)
|
||||
deltaAngle -= 2.0f * M_PI; // -PI..PI
|
||||
|
||||
return fabs(deltaAngle) > M_PI_2;
|
||||
}
|
||||
22
src/Ai/Base/Value/IsBehindValue.h
Normal file
22
src/Ai/Base/Value/IsBehindValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ISBEHINDVALUE_H
|
||||
#define _PLAYERBOT_ISBEHINDVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class IsBehindValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
IsBehindValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
18
src/Ai/Base/Value/IsFacingValue.cpp
Normal file
18
src/Ai/Base/Value/IsFacingValue.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 "IsFacingValue.h"
|
||||
#include <cmath>
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool IsFacingValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
return bot->HasInArc(M_PI_2, target);
|
||||
}
|
||||
22
src/Ai/Base/Value/IsFacingValue.h
Normal file
22
src/Ai/Base/Value/IsFacingValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ISFACINGVALUE_H
|
||||
#define _PLAYERBOT_ISFACINGVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class IsFacingValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
IsFacingValue(PlayerbotAI* botAI, std::string const name = "is facing") : BoolCalculatedValue(botAI, name) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
30
src/Ai/Base/Value/IsMovingValue.cpp
Normal file
30
src/Ai/Base/Value/IsMovingValue.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 "IsMovingValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool IsMovingValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
return target->isMoving();
|
||||
}
|
||||
|
||||
bool IsSwimmingValue::Calculate()
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, qualifier);
|
||||
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
int8 targetInLiquidState = target->GetLiquidData().Status;
|
||||
|
||||
return targetInLiquidState == LIQUID_MAP_UNDER_WATER || (targetInLiquidState == LIQUID_MAP_IN_WATER && target->CanSwim());
|
||||
}
|
||||
30
src/Ai/Base/Value/IsMovingValue.h
Normal file
30
src/Ai/Base/Value/IsMovingValue.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ISMOVINGVALUE_H
|
||||
#define _PLAYERBOT_ISMOVINGVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class IsMovingValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
IsMovingValue(PlayerbotAI* botAI, std::string const name = "is moving") : BoolCalculatedValue(botAI, name) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class IsSwimmingValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
IsSwimmingValue(PlayerbotAI* botAI, std::string const name = "is swimming") : BoolCalculatedValue(botAI, name) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
35
src/Ai/Base/Value/ItemCountValue.cpp
Normal file
35
src/Ai/Base/Value/ItemCountValue.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "ItemCountValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
std::vector<Item*> InventoryItemValueBase::Find(std::string const qualifier)
|
||||
{
|
||||
std::vector<Item*> result;
|
||||
|
||||
Player* bot = InventoryAction::botAI->GetBot();
|
||||
|
||||
std::vector<Item*> items = InventoryAction::parseItems(qualifier);
|
||||
for (Item* item : items)
|
||||
result.push_back(item);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 ItemCountValue::Calculate()
|
||||
{
|
||||
uint32 count = 0;
|
||||
std::vector<Item*> items = Find(qualifier);
|
||||
for (Item* item : items)
|
||||
{
|
||||
count += item->GetCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::vector<Item*> InventoryItemValue::Calculate() { return Find(qualifier); }
|
||||
47
src/Ai/Base/Value/ItemCountValue.h
Normal file
47
src/Ai/Base/Value/ItemCountValue.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ITEMCOUNTVALUE_H
|
||||
#define _PLAYERBOT_ITEMCOUNTVALUE_H
|
||||
|
||||
#include "InventoryAction.h"
|
||||
#include "Item.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class InventoryItemValueBase : public InventoryAction
|
||||
{
|
||||
public:
|
||||
InventoryItemValueBase(PlayerbotAI* botAI) : InventoryAction(botAI, "empty") {}
|
||||
|
||||
bool Execute(Event event) override { return false; }
|
||||
|
||||
protected:
|
||||
std::vector<Item*> Find(std::string const qualifier);
|
||||
};
|
||||
|
||||
class ItemCountValue : public Uint32CalculatedValue, public Qualified, InventoryItemValueBase
|
||||
{
|
||||
public:
|
||||
ItemCountValue(PlayerbotAI* botAI, std::string const name = "inventory items")
|
||||
: Uint32CalculatedValue(botAI, name), InventoryItemValueBase(botAI)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 Calculate() override;
|
||||
};
|
||||
|
||||
class InventoryItemValue : public CalculatedValue<std::vector<Item*>>, public Qualified, InventoryItemValueBase
|
||||
{
|
||||
public:
|
||||
InventoryItemValue(PlayerbotAI* botAI) : CalculatedValue<std::vector<Item*>>(botAI), InventoryItemValueBase(botAI)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<Item*> Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
97
src/Ai/Base/Value/ItemForSpellValue.cpp
Normal file
97
src/Ai/Base/Value/ItemForSpellValue.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 "ItemForSpellValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
#ifndef WIN32
|
||||
inline int strcmpi(char const* s1, char const* s2)
|
||||
{
|
||||
for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2)
|
||||
{
|
||||
}
|
||||
return *s1 - *s2;
|
||||
}
|
||||
#endif
|
||||
|
||||
Item* ItemForSpellValue::Calculate()
|
||||
{
|
||||
uint32 spellid = atoi(qualifier.c_str());
|
||||
if (!spellid)
|
||||
return nullptr;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
|
||||
if (!spellInfo)
|
||||
return nullptr;
|
||||
|
||||
Item* itemForSpell = nullptr;
|
||||
Player* trader = bot->GetTrader();
|
||||
if (trader)
|
||||
{
|
||||
itemForSpell = trader->GetTradeData()->GetItem(TRADE_SLOT_NONTRADED);
|
||||
if (itemForSpell && itemForSpell->IsFitToSpellRequirements(spellInfo))
|
||||
return itemForSpell;
|
||||
}
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
if (master)
|
||||
{
|
||||
trader = master->GetTrader();
|
||||
if (trader)
|
||||
{
|
||||
itemForSpell = trader->GetTradeData()->GetItem(TRADE_SLOT_NONTRADED);
|
||||
if (itemForSpell && itemForSpell->IsFitToSpellRequirements(spellInfo))
|
||||
return itemForSpell;
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround as some spells have no item mask (e.g. shaman weapon enhancements)
|
||||
if (!strcmpi(spellInfo->SpellName[0], "rockbiter weapon") ||
|
||||
!strcmpi(spellInfo->SpellName[0], "flametongue weapon") ||
|
||||
!strcmpi(spellInfo->SpellName[0], "earthliving weapon") ||
|
||||
!strcmpi(spellInfo->SpellName[0], "frostbrand weapon") || !strcmpi(spellInfo->SpellName[0], "windfury weapon"))
|
||||
{
|
||||
itemForSpell = GetItemFitsToSpellRequirements(EQUIPMENT_SLOT_MAINHAND, spellInfo);
|
||||
if (itemForSpell && itemForSpell->GetTemplate()->Class == ITEM_CLASS_WEAPON)
|
||||
return itemForSpell;
|
||||
|
||||
itemForSpell = GetItemFitsToSpellRequirements(EQUIPMENT_SLOT_OFFHAND, spellInfo);
|
||||
if (itemForSpell && itemForSpell->GetTemplate()->Class == ITEM_CLASS_WEAPON)
|
||||
return itemForSpell;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!(spellInfo->Targets & TARGET_FLAG_ITEM))
|
||||
return nullptr;
|
||||
|
||||
if (!strcmpi(spellInfo->SpellName[0], "disenchant"))
|
||||
return nullptr;
|
||||
|
||||
if (!strcmpi(spellInfo->SpellName[0], "pick lock"))
|
||||
return nullptr;
|
||||
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
|
||||
{
|
||||
itemForSpell = GetItemFitsToSpellRequirements(slot, spellInfo);
|
||||
if (itemForSpell)
|
||||
return itemForSpell;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Item* ItemForSpellValue::GetItemFitsToSpellRequirements(uint8 slot, SpellInfo const* spellInfo)
|
||||
{
|
||||
Item* const itemForSpell = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
if (!itemForSpell || itemForSpell->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
return nullptr;
|
||||
|
||||
if (itemForSpell->IsFitToSpellRequirements(spellInfo))
|
||||
return itemForSpell;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
30
src/Ai/Base/Value/ItemForSpellValue.h
Normal file
30
src/Ai/Base/Value/ItemForSpellValue.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ITEMFORSPELLVALUE_H
|
||||
#define _PLAYERBOT_ITEMFORSPELLVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class Item;
|
||||
class PlayerbotAI;
|
||||
class SpellInfo;
|
||||
|
||||
class ItemForSpellValue : public CalculatedValue<Item*>, public Qualified
|
||||
{
|
||||
public:
|
||||
ItemForSpellValue(PlayerbotAI* botAI, std::string const name = "item for spell")
|
||||
: CalculatedValue<Item*>(botAI, name, 1)
|
||||
{
|
||||
}
|
||||
|
||||
Item* Calculate() override;
|
||||
|
||||
private:
|
||||
Item* GetItemFitsToSpellRequirements(uint8 slot, SpellInfo const* spellInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
915
src/Ai/Base/Value/ItemUsageValue.cpp
Normal file
915
src/Ai/Base/Value/ItemUsageValue.cpp
Normal file
@@ -0,0 +1,915 @@
|
||||
/*
|
||||
* 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 "ItemUsageValue.h"
|
||||
|
||||
#include "AiFactory.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "GuildTaskMgr.h"
|
||||
#include "Item.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RandomItemMgr.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "StatsWeightCalculator.h"
|
||||
|
||||
ItemUsage ItemUsageValue::Calculate()
|
||||
{
|
||||
uint32 itemId = 0;
|
||||
uint32 randomPropertyId = 0;
|
||||
size_t pos = qualifier.find(",");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
itemId = atoi(qualifier.substr(0, pos).c_str());
|
||||
randomPropertyId = atoi(qualifier.substr(pos + 1).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
itemId = atoi(qualifier.c_str());
|
||||
}
|
||||
|
||||
if (!itemId)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
if (botAI->HasActivePlayerMaster())
|
||||
{
|
||||
if (IsItemUsefulForSkill(proto) || IsItemNeededForSkill(proto))
|
||||
return ITEM_USAGE_SKILL;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool needItem = false;
|
||||
|
||||
if (IsItemNeededForSkill(proto))
|
||||
needItem = true;
|
||||
else
|
||||
{
|
||||
bool lowBagSpace = AI_VALUE(uint8, "bag space") > 50;
|
||||
|
||||
if (proto->Class == ITEM_CLASS_TRADE_GOODS || proto->Class == ITEM_CLASS_MISC ||
|
||||
proto->Class == ITEM_CLASS_REAGENT)
|
||||
needItem = IsItemNeededForUsefullSpell(proto, lowBagSpace);
|
||||
else if (proto->Class == ITEM_CLASS_RECIPE)
|
||||
{
|
||||
if (bot->HasSpell(proto->Spells[2].SpellId))
|
||||
needItem = false;
|
||||
else
|
||||
needItem = bot->BotCanUseItem(proto) == EQUIP_ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (needItem)
|
||||
{
|
||||
float stacks = CurrentStacks(proto);
|
||||
if (stacks < 1)
|
||||
return ITEM_USAGE_SKILL; // Buy more.
|
||||
if (stacks < 2)
|
||||
return ITEM_USAGE_KEEP; // Keep current amount.
|
||||
}
|
||||
}
|
||||
|
||||
if (proto->Class == ITEM_CLASS_KEY)
|
||||
return ITEM_USAGE_USE;
|
||||
|
||||
if (proto->Class == ITEM_CLASS_CONSUMABLE &&
|
||||
(proto->MaxCount == 0 || bot->GetItemCount(itemId, false) < proto->MaxCount))
|
||||
{
|
||||
std::string const foodType = GetConsumableType(proto, bot->GetPower(POWER_MANA));
|
||||
|
||||
if (!foodType.empty() && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||
{
|
||||
float stacks = BetterStacks(proto, foodType);
|
||||
if (stacks < 2)
|
||||
{
|
||||
stacks += CurrentStacks(proto);
|
||||
|
||||
if (stacks < 2)
|
||||
return ITEM_USAGE_USE; // Buy some to get to 2 stacks
|
||||
else if (stacks < 3) // Keep the item if less than 3 stacks
|
||||
return ITEM_USAGE_KEEP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bot->GetGuildId() && sGuildTaskMgr->IsGuildTaskItem(itemId, bot->GetGuildId()))
|
||||
return ITEM_USAGE_GUILD_TASK;
|
||||
|
||||
ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId);
|
||||
if (equip != ITEM_USAGE_NONE)
|
||||
return equip;
|
||||
|
||||
// Get item instance to check if it's soulbound
|
||||
Item* item = bot->GetItemByEntry(proto->ItemId);
|
||||
bool isSoulbound = item && item->IsSoulBound();
|
||||
|
||||
if ((proto->Class == ITEM_CLASS_ARMOR || proto->Class == ITEM_CLASS_WEAPON) &&
|
||||
botAI->HasSkill(SKILL_ENCHANTING) &&
|
||||
proto->Quality >= ITEM_QUALITY_UNCOMMON)
|
||||
{
|
||||
// Retrieve the bot's Enchanting skill level
|
||||
uint32 enchantingSkill = bot->GetSkillValue(SKILL_ENCHANTING);
|
||||
|
||||
// Check if the bot has a high enough skill to disenchant this item
|
||||
if (proto->RequiredDisenchantSkill > 0 && enchantingSkill < proto->RequiredDisenchantSkill)
|
||||
return ITEM_USAGE_NONE; // Not skilled enough to disenchant
|
||||
|
||||
// BoE (Bind on Equip) items should NOT be disenchanted unless they are already bound
|
||||
if (proto->Bonding == BIND_WHEN_PICKED_UP || (proto->Bonding == BIND_WHEN_EQUIPPED && isSoulbound))
|
||||
return ITEM_USAGE_DISENCHANT;
|
||||
}
|
||||
|
||||
Player* master = botAI->GetMaster();
|
||||
bool isSelfBot = (master == bot);
|
||||
bool botNeedsItemForQuest = IsItemUsefulForQuest(bot, proto);
|
||||
bool masterNeedsItemForQuest = master && sPlayerbotAIConfig->syncQuestWithPlayer && IsItemUsefulForQuest(master, proto);
|
||||
|
||||
// Identify the source of loot
|
||||
LootObject lootObject = AI_VALUE(LootObject, "loot target");
|
||||
|
||||
// Get GUID of loot source
|
||||
ObjectGuid lootGuid = lootObject.guid;
|
||||
|
||||
// Check if loot source is an item
|
||||
bool isLootFromItem = lootGuid.IsItem();
|
||||
|
||||
// If the loot is from an item in the bot’s bags, ignore syncQuestWithPlayer
|
||||
if (isLootFromItem && botNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_QUEST;
|
||||
}
|
||||
|
||||
// If the bot is NOT acting alone and the master needs this quest item, defer to the master
|
||||
if (!isSelfBot && masterNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
// If the bot itself needs the item for a quest, allow looting
|
||||
if (botNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_QUEST;
|
||||
}
|
||||
|
||||
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||
{
|
||||
if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_ROGUE || bot->getClass() == CLASS_WARRIOR)
|
||||
{
|
||||
Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||
uint32 requiredSubClass = 0;
|
||||
|
||||
if (rangedWeapon)
|
||||
{
|
||||
switch (rangedWeapon->GetTemplate()->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
requiredSubClass = ITEM_SUBCLASS_BULLET;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
requiredSubClass = ITEM_SUBCLASS_ARROW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the item is the correct ammo type for the equipped ranged weapon
|
||||
if (proto->SubClass == requiredSubClass)
|
||||
{
|
||||
float ammoCount = BetterStacks(proto, "ammo");
|
||||
float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2
|
||||
uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID);
|
||||
|
||||
// Check if the bot has an ammo type assigned
|
||||
if (currentAmmoId == 0)
|
||||
{
|
||||
return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo
|
||||
}
|
||||
// Compare new ammo vs current equipped ammo
|
||||
ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId);
|
||||
if (currentAmmoProto)
|
||||
{
|
||||
uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2;
|
||||
uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2;
|
||||
|
||||
if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition
|
||||
{
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
if (newAmmoDPS < currentAmmoDPS) // New ammo is worse
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
// Ensure we have enough ammo in the inventory
|
||||
if (ammoCount < requiredAmmo)
|
||||
{
|
||||
ammoCount += CurrentStacks(proto);
|
||||
|
||||
if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply
|
||||
return ITEM_USAGE_AMMO;
|
||||
else if (ammoCount < requiredAmmo + 1)
|
||||
return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to add something like free bagspace or item value.
|
||||
if (proto->SellPrice > 0)
|
||||
{
|
||||
if (proto->Quality >= ITEM_QUALITY_NORMAL && !isSoulbound)
|
||||
{
|
||||
return ITEM_USAGE_AH;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ITEM_USAGE_VENDOR;
|
||||
}
|
||||
}
|
||||
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, int32 randomPropertyId)
|
||||
{
|
||||
if (bot->BotCanUseItem(itemProto) != EQUIP_ERR_OK)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
if (itemProto->InventoryType == INVTYPE_NON_EQUIP)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
Item* pItem = Item::CreateItem(itemProto->ItemId, 1, bot, false, 0, true);
|
||||
if (!pItem)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
uint16 dest;
|
||||
InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, true);
|
||||
pItem->RemoveFromUpdateQueueOf(bot);
|
||||
delete pItem;
|
||||
|
||||
if (result != EQUIP_ERR_OK && result != EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
// Check is unique items are equipped or not
|
||||
bool needToCheckUnique = false;
|
||||
if (result == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
|
||||
{
|
||||
needToCheckUnique = true;
|
||||
}
|
||||
else if (itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE))
|
||||
{
|
||||
needToCheckUnique = true;
|
||||
}
|
||||
|
||||
if (needToCheckUnique)
|
||||
{
|
||||
// Count the total number of the item (equipped + in bags)
|
||||
uint32 totalItemCount = bot->GetItemCount(itemProto->ItemId, true);
|
||||
|
||||
// Count the number of the item in bags only
|
||||
uint32 bagItemCount = bot->GetItemCount(itemProto->ItemId, false);
|
||||
|
||||
// Determine if the unique item is already equipped
|
||||
bool isEquipped = (totalItemCount > bagItemCount);
|
||||
|
||||
if (isEquipped)
|
||||
{
|
||||
return ITEM_USAGE_NONE; // Item is already equipped
|
||||
}
|
||||
// If not equipped, continue processing
|
||||
}
|
||||
|
||||
if (itemProto->Class == ITEM_CLASS_QUIVER)
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
if (itemProto->Class == ITEM_CLASS_CONTAINER)
|
||||
{
|
||||
if (itemProto->SubClass != ITEM_SUBCLASS_CONTAINER)
|
||||
return ITEM_USAGE_NONE; // Todo add logic for non-bag containers. We want to look at professions/class and
|
||||
// only replace if non-bag is larger than bag.
|
||||
|
||||
if (GetSmallestBagSize() >= itemProto->ContainerSlots)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
|
||||
bool shouldEquip = false;
|
||||
// uint32 statWeight = sRandomItemMgr->GetLiveStatWeight(bot, itemProto->ItemId);
|
||||
StatsWeightCalculator calculator(bot);
|
||||
calculator.SetItemSetBonus(false);
|
||||
calculator.SetOverflowPenalty(false);
|
||||
|
||||
float itemScore = calculator.CalculateItem(itemProto->ItemId, randomPropertyId);
|
||||
|
||||
if (itemScore)
|
||||
shouldEquip = true;
|
||||
|
||||
if (itemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr->CanEquipWeapon(bot->getClass(), itemProto))
|
||||
shouldEquip = false;
|
||||
if (itemProto->Class == ITEM_CLASS_ARMOR &&
|
||||
!sRandomItemMgr->CanEquipArmor(bot->getClass(), bot->GetLevel(), itemProto))
|
||||
shouldEquip = false;
|
||||
|
||||
uint8 possibleSlots = 1;
|
||||
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true);
|
||||
// Check if dest wasn't set correctly by CanEquipItem and use FindEquipSlot instead
|
||||
// This occurs with unique items that are already in the bots bags when CanEquipItem is called
|
||||
if (dest == 0)
|
||||
{
|
||||
if (dstSlot != NULL_SLOT)
|
||||
{
|
||||
// Construct dest from dstSlot
|
||||
dest = (INVENTORY_SLOT_BAG_0 << 8) | dstSlot;
|
||||
}
|
||||
}
|
||||
|
||||
if (dstSlot == EQUIPMENT_SLOT_FINGER1 || dstSlot == EQUIPMENT_SLOT_TRINKET1)
|
||||
{
|
||||
possibleSlots = 2;
|
||||
}
|
||||
|
||||
// Check weapon case separately to keep things a bit cleaner
|
||||
bool have2HWeapon = false;
|
||||
bool isValidTGWeapon = false;
|
||||
|
||||
if (dstSlot == EQUIPMENT_SLOT_MAINHAND)
|
||||
{
|
||||
Item* currentWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||
have2HWeapon = currentWeapon && currentWeapon->GetTemplate()->InventoryType == INVTYPE_2HWEAPON;
|
||||
|
||||
// Determine if the new weapon is a valid Titan Grip weapon
|
||||
isValidTGWeapon = (itemProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 ||
|
||||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 ||
|
||||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
|
||||
|
||||
// If the bot can Titan Grip, ignore any 2H weapon that isn't a 2H sword, mace, or axe.
|
||||
if (bot->CanTitanGrip())
|
||||
{
|
||||
// If this weapon is 2H but not one of the valid TG weapon types, do not equip it at all.
|
||||
if (itemProto->InventoryType == INVTYPE_2HWEAPON && !isValidTGWeapon)
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Now handle the logic for equipping and possible offhand slots
|
||||
// If the bot can Dual Wield and:
|
||||
// - The weapon is not 2H and we currently don't have a 2H weapon equipped
|
||||
// OR
|
||||
// - The bot can Titan Grip and it is a valid TG weapon
|
||||
// Then we can consider the offhand slot as well.
|
||||
if (bot->CanDualWield() &&
|
||||
((itemProto->InventoryType != INVTYPE_2HWEAPON && !have2HWeapon) ||
|
||||
(bot->CanTitanGrip() && isValidTGWeapon)))
|
||||
{
|
||||
possibleSlots = 2;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < possibleSlots; i++)
|
||||
{
|
||||
bool shouldEquipInSlot = shouldEquip;
|
||||
Item* oldItem = bot->GetItemByPos(dest + i);
|
||||
|
||||
// No item equipped
|
||||
if (!oldItem)
|
||||
{
|
||||
if (shouldEquipInSlot)
|
||||
return ITEM_USAGE_EQUIP;
|
||||
else
|
||||
{
|
||||
return ITEM_USAGE_BAD_EQUIP;
|
||||
}
|
||||
}
|
||||
|
||||
ItemTemplate const* oldItemProto = oldItem->GetTemplate();
|
||||
float oldScore = calculator.CalculateItem(oldItemProto->ItemId, oldItem->GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID));
|
||||
if (oldItem)
|
||||
{
|
||||
// uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId);
|
||||
if (itemScore || oldScore)
|
||||
{
|
||||
shouldEquipInSlot = itemScore > oldScore * sPlayerbotAIConfig->equipUpgradeThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
// Bigger quiver
|
||||
if (itemProto->Class == ITEM_CLASS_QUIVER)
|
||||
{
|
||||
if (!oldItem || oldItemProto->ContainerSlots < itemProto->ContainerSlots)
|
||||
{
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool existingShouldEquip = true;
|
||||
if (oldItemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr->CanEquipWeapon(bot->getClass(), oldItemProto))
|
||||
existingShouldEquip = false;
|
||||
|
||||
if (oldItemProto->Class == ITEM_CLASS_ARMOR &&
|
||||
!sRandomItemMgr->CanEquipArmor(bot->getClass(), bot->GetLevel(), oldItemProto))
|
||||
existingShouldEquip = false;
|
||||
|
||||
// uint32 oldItemPower = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId);
|
||||
// uint32 newItemPower = sRandomItemMgr->GetLiveStatWeight(bot, itemProto->ItemId);
|
||||
|
||||
// Compare items based on item level, quality or itemId.
|
||||
bool isBetter = false;
|
||||
if (itemScore > oldScore)
|
||||
isBetter = true;
|
||||
// else if (newItemPower == oldScore && itemProto->Quality > oldItemProto->Quality)
|
||||
// isBetter = true;
|
||||
// else if (newItemPower == oldScore && itemProto->Quality == oldItemProto->Quality && itemProto->ItemId >
|
||||
// oldItemProto->ItemId)
|
||||
// isBetter = true;
|
||||
|
||||
Item* item = CurrentItem(itemProto);
|
||||
bool itemIsBroken =
|
||||
item && item->GetUInt32Value(ITEM_FIELD_DURABILITY) == 0 && item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0;
|
||||
bool oldItemIsBroken =
|
||||
oldItem->GetUInt32Value(ITEM_FIELD_DURABILITY) == 0 && oldItem->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0;
|
||||
|
||||
if (itemProto->ItemId != oldItemProto->ItemId && (shouldEquipInSlot || !existingShouldEquip) && isBetter)
|
||||
{
|
||||
switch (itemProto->Class)
|
||||
{
|
||||
case ITEM_CLASS_ARMOR:
|
||||
if (oldItemProto->SubClass <= itemProto->SubClass)
|
||||
{
|
||||
// Need to add some logic to check second slot before returning, but as it happens, all three of these
|
||||
// return vals will result in an attempted equip action so it wouldn't have much effect currently
|
||||
if (itemIsBroken && !oldItemIsBroken)
|
||||
return ITEM_USAGE_BROKEN_EQUIP;
|
||||
else if (shouldEquipInSlot)
|
||||
return ITEM_USAGE_REPLACE;
|
||||
else
|
||||
return ITEM_USAGE_BAD_EQUIP;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (itemIsBroken && !oldItemIsBroken)
|
||||
return ITEM_USAGE_BROKEN_EQUIP;
|
||||
else if (shouldEquipInSlot)
|
||||
return ITEM_USAGE_EQUIP;
|
||||
else
|
||||
return ITEM_USAGE_BAD_EQUIP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Item is not better but current item is broken and new one is not.
|
||||
if (oldItemIsBroken && !itemIsBroken)
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
// Return smaltest bag size equipped
|
||||
uint32 ItemUsageValue::GetSmallestBagSize()
|
||||
{
|
||||
int8 curSlot = 0;
|
||||
uint32 curSlots = 0;
|
||||
for (uint8 bag = INVENTORY_SLOT_BAG_START + 1; bag < INVENTORY_SLOT_BAG_END; ++bag)
|
||||
{
|
||||
if (Bag const* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag))
|
||||
{
|
||||
if (curSlot > 0 && curSlots < pBag->GetBagSize())
|
||||
continue;
|
||||
|
||||
curSlot = pBag->GetSlot();
|
||||
curSlots = pBag->GetBagSize();
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
return curSlots;
|
||||
}
|
||||
|
||||
bool ItemUsageValue::IsItemUsefulForQuest(Player* player, ItemTemplate const* proto)
|
||||
{
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (!botAI)
|
||||
return false;
|
||||
|
||||
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
|
||||
{
|
||||
uint32 entry = player->GetQuestSlotQuestId(slot);
|
||||
Quest const* quest = sObjectMgr->GetQuestTemplate(entry);
|
||||
if (!quest)
|
||||
continue;
|
||||
|
||||
// Check if the item itself is needed for the quest
|
||||
for (uint8 i = 0; i < 4; i++)
|
||||
{
|
||||
if (quest->RequiredItemId[i] == proto->ItemId)
|
||||
{
|
||||
if (player->GetItemCount(proto->ItemId, false) >= quest->RequiredItemCount[i])
|
||||
continue;
|
||||
|
||||
return true; // Item is directly required for a quest
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the item has spells that create a required quest item
|
||||
for (uint8 i = 0; i < MAX_ITEM_SPELLS; i++)
|
||||
{
|
||||
uint32 spellId = proto->Spells[i].SpellId;
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
for (uint8 effectIndex = 0; effectIndex < MAX_SPELL_EFFECTS; effectIndex++)
|
||||
{
|
||||
if (spellInfo->Effects[effectIndex].Effect == SPELL_EFFECT_CREATE_ITEM)
|
||||
{
|
||||
uint32 createdItemId = spellInfo->Effects[effectIndex].ItemType;
|
||||
|
||||
// Check if the created item is required for a quest
|
||||
for (uint8 j = 0; j < 4; j++)
|
||||
{
|
||||
if (quest->RequiredItemId[j] == createdItemId)
|
||||
{
|
||||
if (player->GetItemCount(createdItemId, false) >= quest->RequiredItemCount[j])
|
||||
continue;
|
||||
|
||||
return true; // Item is useful because it creates a required quest item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Item is not useful for any active quests
|
||||
}
|
||||
|
||||
bool ItemUsageValue::IsItemNeededForSkill(ItemTemplate const* proto)
|
||||
{
|
||||
switch (proto->ItemId)
|
||||
{
|
||||
case 756: // Tunnel Pick
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 778: // Kobold Excavation Pick
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 1819: // Gouging Pick
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 1893: // Miner's Revenge
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 1959: // Cold Iron Pick
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 2901: // Mining Pick
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 9465: // Digmaster 5000
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 20723: // Brann's Trusty Pick
|
||||
return botAI->HasSkill(SKILL_MINING);
|
||||
case 40772: // Gnomish Army Knife
|
||||
return botAI->HasSkill(SKILL_MINING) || botAI->HasSkill(SKILL_ENGINEERING) || botAI->HasSkill(SKILL_BLACKSMITHING) || botAI->HasSkill(SKILL_COOKING) || botAI->HasSkill(SKILL_SKINNING);
|
||||
case 40892: // Hammer Pick
|
||||
return botAI->HasSkill(SKILL_MINING) || botAI->HasSkill(SKILL_BLACKSMITHING);
|
||||
case 40893: // Bladed Pickaxe
|
||||
return botAI->HasSkill(SKILL_MINING) || botAI->HasSkill(SKILL_SKINNING);
|
||||
case 5956: // Blacksmith Hammer
|
||||
return botAI->HasSkill(SKILL_BLACKSMITHING) || botAI->HasSkill(SKILL_ENGINEERING);
|
||||
case 6219: // Arclight Spanner
|
||||
return botAI->HasSkill(SKILL_ENGINEERING);
|
||||
case 6218: // Runed copper rod
|
||||
return botAI->HasSkill(SKILL_ENCHANTING);
|
||||
case 6339: // Runed silver rod
|
||||
return botAI->HasSkill(SKILL_ENCHANTING);
|
||||
case 11130: // Runed golden rod
|
||||
return botAI->HasSkill(SKILL_ENCHANTING);
|
||||
case 11145: // Runed truesilver rod
|
||||
return botAI->HasSkill(SKILL_ENCHANTING);
|
||||
case 16207: // Runed Arcanite Rod
|
||||
return botAI->HasSkill(SKILL_ENCHANTING);
|
||||
case 7005: // Skinning Knife
|
||||
return botAI->HasSkill(SKILL_SKINNING);
|
||||
case 12709:
|
||||
return botAI->HasSkill(SKILL_SKINNING);
|
||||
case 19901:
|
||||
return botAI->HasSkill(SKILL_SKINNING);
|
||||
case 4471: // Flint and Tinder
|
||||
return botAI->HasSkill(SKILL_COOKING);
|
||||
case 4470: // Simple Wood
|
||||
return botAI->HasSkill(SKILL_COOKING);
|
||||
case 6256: // Fishing Rod
|
||||
return botAI->HasSkill(SKILL_FISHING);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ItemUsageValue::IsItemUsefulForSkill(ItemTemplate const* proto)
|
||||
{
|
||||
switch (proto->Class)
|
||||
{
|
||||
case ITEM_CLASS_TRADE_GOODS:
|
||||
case ITEM_CLASS_MISC:
|
||||
case ITEM_CLASS_REAGENT:
|
||||
case ITEM_CLASS_GEM:
|
||||
{
|
||||
if (botAI->HasSkill(SKILL_TAILORING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_TAILORING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_LEATHERWORKING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_LEATHERWORKING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_ENGINEERING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_ENGINEERING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_BLACKSMITHING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_BLACKSMITHING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_ALCHEMY) && RandomItemMgr::IsUsedBySkill(proto, SKILL_ALCHEMY))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_ENCHANTING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_ENCHANTING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_FISHING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_FISHING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_FIRST_AID) && RandomItemMgr::IsUsedBySkill(proto, SKILL_FIRST_AID))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_COOKING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_COOKING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_JEWELCRAFTING) && RandomItemMgr::IsUsedBySkill(proto, SKILL_JEWELCRAFTING))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_MINING) && (RandomItemMgr::IsUsedBySkill(proto, SKILL_MINING) ||
|
||||
RandomItemMgr::IsUsedBySkill(proto, SKILL_BLACKSMITHING) ||
|
||||
RandomItemMgr::IsUsedBySkill(proto, SKILL_JEWELCRAFTING) ||
|
||||
RandomItemMgr::IsUsedBySkill(proto, SKILL_ENGINEERING)))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_SKINNING) && (RandomItemMgr::IsUsedBySkill(proto, SKILL_SKINNING) ||
|
||||
RandomItemMgr::IsUsedBySkill(proto, SKILL_LEATHERWORKING)))
|
||||
return true;
|
||||
if (botAI->HasSkill(SKILL_HERBALISM) && (RandomItemMgr::IsUsedBySkill(proto, SKILL_HERBALISM) ||
|
||||
RandomItemMgr::IsUsedBySkill(proto, SKILL_ALCHEMY)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
case ITEM_CLASS_RECIPE:
|
||||
{
|
||||
if (bot->HasSpell(proto->Spells[2].SpellId))
|
||||
break;
|
||||
|
||||
switch (proto->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_LEATHERWORKING_PATTERN:
|
||||
return botAI->HasSkill(SKILL_LEATHERWORKING);
|
||||
case ITEM_SUBCLASS_TAILORING_PATTERN:
|
||||
return botAI->HasSkill(SKILL_TAILORING);
|
||||
case ITEM_SUBCLASS_ENGINEERING_SCHEMATIC:
|
||||
return botAI->HasSkill(SKILL_ENGINEERING);
|
||||
case ITEM_SUBCLASS_BLACKSMITHING:
|
||||
return botAI->HasSkill(SKILL_BLACKSMITHING);
|
||||
case ITEM_SUBCLASS_COOKING_RECIPE:
|
||||
return botAI->HasSkill(SKILL_COOKING);
|
||||
case ITEM_SUBCLASS_ALCHEMY_RECIPE:
|
||||
return botAI->HasSkill(SKILL_ALCHEMY);
|
||||
case ITEM_SUBCLASS_FIRST_AID_MANUAL:
|
||||
return botAI->HasSkill(SKILL_FIRST_AID);
|
||||
case ITEM_SUBCLASS_ENCHANTING_FORMULA:
|
||||
return botAI->HasSkill(SKILL_ENCHANTING);
|
||||
case ITEM_SUBCLASS_FISHING_MANUAL:
|
||||
return botAI->HasSkill(SKILL_FISHING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ItemUsageValue::IsItemNeededForUsefullSpell(ItemTemplate const* proto, bool checkAllReagents)
|
||||
{
|
||||
for (auto spellId : SpellsUsingItem(proto->ItemId, bot))
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (checkAllReagents && !HasItemsNeededForSpell(spellId, proto))
|
||||
continue;
|
||||
|
||||
if (SpellGivesSkillUp(spellId, bot))
|
||||
return true;
|
||||
|
||||
uint32 newItemId = spellInfo->Effects[EFFECT_0].ItemType;
|
||||
if (newItemId && newItemId != proto->ItemId)
|
||||
{
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", newItemId);
|
||||
|
||||
if (usage != ITEM_USAGE_REPLACE && usage != ITEM_USAGE_EQUIP && usage != ITEM_USAGE_AMMO &&
|
||||
usage != ITEM_USAGE_QUEST && usage != ITEM_USAGE_SKILL && usage != ITEM_USAGE_USE)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ItemUsageValue::HasItemsNeededForSpell(uint32 spellId, ItemTemplate const* proto)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_REAGENTS; i++)
|
||||
if (spellInfo->ReagentCount[i] > 0 && spellInfo->Reagent[i])
|
||||
{
|
||||
if (proto && proto->ItemId == spellInfo->Reagent[i] &&
|
||||
spellInfo->ReagentCount[i] == 1) // If we only need 1 item then current item does not need to be
|
||||
// checked since we are looting/buying or already have it.
|
||||
continue;
|
||||
|
||||
ItemTemplate const* reqProto = sObjectMgr->GetItemTemplate(spellInfo->Reagent[i]);
|
||||
|
||||
uint32 count = AI_VALUE2(uint32, "item count", reqProto->Name1);
|
||||
|
||||
if (count < spellInfo->ReagentCount[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Item* ItemUsageValue::CurrentItem(ItemTemplate const* proto)
|
||||
{
|
||||
Item* bestItem = nullptr;
|
||||
std::vector<Item*> found = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatItem(proto));
|
||||
for (auto item : found)
|
||||
{
|
||||
if (bestItem && item->GetUInt32Value(ITEM_FIELD_DURABILITY) < bestItem->GetUInt32Value(ITEM_FIELD_DURABILITY))
|
||||
continue;
|
||||
|
||||
if (bestItem && item->GetCount() < bestItem->GetCount())
|
||||
continue;
|
||||
|
||||
bestItem = item;
|
||||
}
|
||||
|
||||
return bestItem;
|
||||
}
|
||||
|
||||
float ItemUsageValue::CurrentStacks(ItemTemplate const* proto)
|
||||
{
|
||||
uint32 maxStack = proto->GetMaxStackSize();
|
||||
|
||||
std::vector<Item*> found = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatItem(proto));
|
||||
|
||||
float itemCount = 0;
|
||||
|
||||
for (auto stack : found)
|
||||
{
|
||||
itemCount += stack->GetCount();
|
||||
}
|
||||
|
||||
return itemCount / maxStack;
|
||||
}
|
||||
|
||||
float ItemUsageValue::BetterStacks(ItemTemplate const* proto, std::string const itemType)
|
||||
{
|
||||
std::vector<Item*> items = AI_VALUE2(std::vector<Item*>, "inventory items", itemType);
|
||||
|
||||
float stacks = 0;
|
||||
|
||||
for (auto& otherItem : items)
|
||||
{
|
||||
ItemTemplate const* otherProto = otherItem->GetTemplate();
|
||||
|
||||
if (otherProto->Class != proto->Class || otherProto->SubClass != proto->SubClass)
|
||||
continue;
|
||||
|
||||
if (otherProto->ItemLevel < proto->ItemLevel)
|
||||
continue;
|
||||
|
||||
if (otherProto->ItemId == proto->ItemId)
|
||||
continue;
|
||||
|
||||
stacks += CurrentStacks(otherProto);
|
||||
}
|
||||
|
||||
return stacks;
|
||||
}
|
||||
|
||||
std::vector<uint32> ItemUsageValue::SpellsUsingItem(uint32 itemId, Player* bot)
|
||||
{
|
||||
std::vector<uint32> retSpells;
|
||||
|
||||
PlayerSpellMap const& spellMap = bot->GetSpellMap();
|
||||
|
||||
for (auto& spell : spellMap)
|
||||
{
|
||||
uint32 spellId = spell.first;
|
||||
|
||||
if (spell.second->State == PLAYERSPELL_REMOVED || !spell.second->Active)
|
||||
continue;
|
||||
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (spellInfo->IsPassive())
|
||||
continue;
|
||||
|
||||
if (spellInfo->Effects[EFFECT_0].Effect != SPELL_EFFECT_CREATE_ITEM)
|
||||
continue;
|
||||
|
||||
for (uint8 i = 0; i < MAX_SPELL_REAGENTS; i++)
|
||||
if (spellInfo->ReagentCount[i] > 0 && spellInfo->Reagent[i] == itemId)
|
||||
retSpells.push_back(spellId);
|
||||
}
|
||||
|
||||
return retSpells;
|
||||
}
|
||||
|
||||
inline int32 SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
|
||||
{
|
||||
if (SkillValue >= GrayLevel)
|
||||
return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_GREY) * 10;
|
||||
|
||||
if (SkillValue >= GreenLevel)
|
||||
return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_GREEN) * 10;
|
||||
|
||||
if (SkillValue >= YellowLevel)
|
||||
return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_YELLOW) * 10;
|
||||
|
||||
return sWorld->getIntConfig(CONFIG_SKILL_CHANCE_ORANGE) * 10;
|
||||
}
|
||||
|
||||
bool ItemUsageValue::SpellGivesSkillUp(uint32 spellId, Player* bot)
|
||||
{
|
||||
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
|
||||
|
||||
for (SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
|
||||
{
|
||||
SkillLineAbilityEntry const* skill = _spell_idx->second;
|
||||
if (skill->SkillLine)
|
||||
{
|
||||
uint32 SkillValue = bot->GetPureSkillValue(skill->SkillLine);
|
||||
|
||||
uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING);
|
||||
|
||||
if (SkillGainChance(SkillValue, skill->TrivialSkillLineRankHigh,
|
||||
(skill->TrivialSkillLineRankHigh + skill->TrivialSkillLineRankLow) / 2,
|
||||
skill->TrivialSkillLineRankLow) > 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string const ItemUsageValue::GetConsumableType(ItemTemplate const* proto, bool hasMana)
|
||||
{
|
||||
std::string const foodType = "";
|
||||
|
||||
if ((proto->SubClass == ITEM_SUBCLASS_CONSUMABLE || proto->SubClass == ITEM_SUBCLASS_FOOD))
|
||||
{
|
||||
if (proto->Spells[0].SpellCategory == 11)
|
||||
return "food";
|
||||
else if (proto->Spells[0].SpellCategory == 59 && hasMana)
|
||||
return "drink";
|
||||
}
|
||||
|
||||
if (proto->SubClass == ITEM_SUBCLASS_POTION || proto->SubClass == ITEM_SUBCLASS_FLASK)
|
||||
{
|
||||
for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
|
||||
{
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
|
||||
if (spellInfo)
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_ENERGIZE && hasMana)
|
||||
return "mana potion";
|
||||
|
||||
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_HEAL)
|
||||
return "healing potion";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (proto->SubClass == ITEM_SUBCLASS_BANDAGE)
|
||||
{
|
||||
return "bandage";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
64
src/Ai/Base/Value/ItemUsageValue.h
Normal file
64
src/Ai/Base/Value/ItemUsageValue.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_ITEMUSAGEVALUE_H
|
||||
#define _PLAYERBOT_ITEMUSAGEVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class Item;
|
||||
class Player;
|
||||
class PlayerbotAI;
|
||||
|
||||
struct ItemTemplate;
|
||||
|
||||
enum ItemUsage : uint32
|
||||
{
|
||||
ITEM_USAGE_NONE = 0,
|
||||
ITEM_USAGE_EQUIP = 1,
|
||||
ITEM_USAGE_REPLACE = 2,
|
||||
ITEM_USAGE_BAD_EQUIP = 3,
|
||||
ITEM_USAGE_BROKEN_EQUIP = 4,
|
||||
ITEM_USAGE_QUEST = 5,
|
||||
ITEM_USAGE_SKILL = 6,
|
||||
ITEM_USAGE_USE = 7,
|
||||
ITEM_USAGE_GUILD_TASK = 8,
|
||||
ITEM_USAGE_DISENCHANT = 9,
|
||||
ITEM_USAGE_AH = 10,
|
||||
ITEM_USAGE_KEEP = 11,
|
||||
ITEM_USAGE_VENDOR = 12,
|
||||
ITEM_USAGE_AMMO = 13
|
||||
};
|
||||
|
||||
class ItemUsageValue : public CalculatedValue<ItemUsage>, public Qualified
|
||||
{
|
||||
public:
|
||||
ItemUsageValue(PlayerbotAI* botAI, std::string const name = "item usage") : CalculatedValue<ItemUsage>(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
ItemUsage Calculate() override;
|
||||
|
||||
private:
|
||||
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0);
|
||||
uint32 GetSmallestBagSize();
|
||||
bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto);
|
||||
bool IsItemNeededForSkill(ItemTemplate const* proto);
|
||||
bool IsItemUsefulForSkill(ItemTemplate const* proto);
|
||||
bool IsItemNeededForUsefullSpell(ItemTemplate const* proto, bool checkAllReagents = false);
|
||||
bool HasItemsNeededForSpell(uint32 spellId, ItemTemplate const* proto);
|
||||
Item* CurrentItem(ItemTemplate const* proto);
|
||||
float CurrentStacks(ItemTemplate const* proto);
|
||||
float BetterStacks(ItemTemplate const* proto, std::string const usageType = "");
|
||||
|
||||
public:
|
||||
static std::vector<uint32> SpellsUsingItem(uint32 itemId, Player* bot);
|
||||
static bool SpellGivesSkillUp(uint32 spellId, Player* bot);
|
||||
|
||||
static std::string const GetConsumableType(ItemTemplate const* proto, bool hasMana);
|
||||
};
|
||||
|
||||
#endif
|
||||
76
src/Ai/Base/Value/LastMovementValue.cpp
Normal file
76
src/Ai/Base/Value/LastMovementValue.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 "LastMovementValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "Timer.h"
|
||||
|
||||
LastMovement::LastMovement() { clear(); }
|
||||
|
||||
LastMovement::LastMovement(LastMovement& other)
|
||||
: taxiNodes(other.taxiNodes),
|
||||
taxiMaster(other.taxiMaster),
|
||||
lastFollow(other.lastFollow),
|
||||
lastAreaTrigger(other.lastAreaTrigger),
|
||||
lastMoveToX(other.lastMoveToX),
|
||||
lastMoveToY(other.lastMoveToY),
|
||||
lastMoveToZ(other.lastMoveToZ),
|
||||
lastMoveToOri(other.lastMoveToOri),
|
||||
lastFlee(other.lastFlee)
|
||||
{
|
||||
lastMoveShort = other.lastMoveShort;
|
||||
nextTeleport = other.nextTeleport;
|
||||
lastPath = other.lastPath;
|
||||
priority = other.priority;
|
||||
}
|
||||
|
||||
void LastMovement::clear()
|
||||
{
|
||||
lastMoveShort = WorldPosition();
|
||||
lastPath.clear();
|
||||
lastMoveToMapId = 0;
|
||||
lastMoveToX = 0;
|
||||
lastMoveToY = 0;
|
||||
lastMoveToZ = 0;
|
||||
lastMoveToOri = 0;
|
||||
lastFollow = nullptr;
|
||||
lastAreaTrigger = 0;
|
||||
lastFlee = 0;
|
||||
nextTeleport = 0;
|
||||
msTime = 0;
|
||||
lastdelayTime = 0;
|
||||
priority = MovementPriority::MOVEMENT_NORMAL;
|
||||
}
|
||||
|
||||
void LastMovement::Set(Unit* follow)
|
||||
{
|
||||
Set(0, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
|
||||
setShort(WorldPosition());
|
||||
setPath(TravelPath());
|
||||
lastFollow = follow;
|
||||
}
|
||||
|
||||
void LastMovement::Set(uint32 mapId, float x, float y, float z, float ori, float delayTime, MovementPriority pri)
|
||||
{
|
||||
lastMoveToMapId = mapId;
|
||||
lastMoveToX = x;
|
||||
lastMoveToY = y;
|
||||
lastMoveToZ = z;
|
||||
lastMoveToOri = ori;
|
||||
lastFollow = nullptr;
|
||||
lastMoveShort = WorldPosition(mapId, x, y, z, ori);
|
||||
msTime = getMSTime();
|
||||
lastdelayTime = delayTime;
|
||||
priority = pri;
|
||||
}
|
||||
|
||||
void LastMovement::setShort(WorldPosition point)
|
||||
{
|
||||
lastMoveShort = point;
|
||||
lastFollow = nullptr;
|
||||
}
|
||||
|
||||
void LastMovement::setPath(TravelPath path) { lastPath = path; }
|
||||
87
src/Ai/Base/Value/LastMovementValue.h
Normal file
87
src/Ai/Base/Value/LastMovementValue.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LASTMOVEMENTVALUE_H
|
||||
#define _PLAYERBOT_LASTMOVEMENTVALUE_H
|
||||
|
||||
#include "ObjectGuid.h"
|
||||
#include "TravelNode.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
// High priority movement can override the previous low priority one
|
||||
enum class MovementPriority
|
||||
{
|
||||
MOVEMENT_IDLE,
|
||||
MOVEMENT_WANDER,
|
||||
MOVEMENT_NORMAL,
|
||||
MOVEMENT_COMBAT,
|
||||
MOVEMENT_FORCED
|
||||
};
|
||||
|
||||
class LastMovement
|
||||
{
|
||||
public:
|
||||
LastMovement();
|
||||
LastMovement(LastMovement& other);
|
||||
|
||||
LastMovement& operator=(LastMovement const& other)
|
||||
{
|
||||
taxiNodes = other.taxiNodes;
|
||||
taxiMaster = other.taxiMaster;
|
||||
lastFollow = other.lastFollow;
|
||||
lastAreaTrigger = other.lastAreaTrigger;
|
||||
lastMoveShort = other.lastMoveShort;
|
||||
lastPath = other.lastPath;
|
||||
nextTeleport = other.nextTeleport;
|
||||
priority = other.priority;
|
||||
return *this;
|
||||
};
|
||||
|
||||
void clear();
|
||||
|
||||
void Set(Unit* follow);
|
||||
void Set(uint32 mapId, float x, float y, float z, float ori, float delayTime, MovementPriority priority = MovementPriority::MOVEMENT_NORMAL);
|
||||
|
||||
void setShort(WorldPosition point);
|
||||
void setPath(TravelPath path);
|
||||
|
||||
std::vector<uint32> taxiNodes;
|
||||
ObjectGuid taxiMaster;
|
||||
Unit* lastFollow;
|
||||
uint32 lastAreaTrigger;
|
||||
time_t lastFlee;
|
||||
uint32 lastMoveToMapId;
|
||||
float lastMoveToX;
|
||||
float lastMoveToY;
|
||||
float lastMoveToZ;
|
||||
float lastMoveToOri;
|
||||
float lastdelayTime;
|
||||
WorldPosition lastMoveShort;
|
||||
uint32 msTime;
|
||||
MovementPriority priority;
|
||||
TravelPath lastPath;
|
||||
time_t nextTeleport;
|
||||
std::future<TravelPath> future;
|
||||
};
|
||||
|
||||
class LastMovementValue : public ManualSetValue<LastMovement&>
|
||||
{
|
||||
public:
|
||||
LastMovementValue(PlayerbotAI* botAI) : ManualSetValue<LastMovement&>(botAI, data) {}
|
||||
|
||||
private:
|
||||
LastMovement data = LastMovement();
|
||||
};
|
||||
|
||||
class StayTimeValue : public ManualSetValue<time_t>
|
||||
{
|
||||
public:
|
||||
StayTimeValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, 0) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
26
src/Ai/Base/Value/LastSaidValue.h
Normal file
26
src/Ai/Base/Value/LastSaidValue.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LASTSAIDVALUE_H
|
||||
#define _PLAYERBOT_LASTSAIDVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class LastSaidValue : public ManualSetValue<time_t>, public Qualified
|
||||
{
|
||||
public:
|
||||
LastSaidValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, time(nullptr) - 120, "last said") {}
|
||||
};
|
||||
|
||||
class LastEmoteValue : public ManualSetValue<time_t>, public Qualified
|
||||
{
|
||||
public:
|
||||
LastEmoteValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, time(nullptr) - 120, "last emote") {}
|
||||
};
|
||||
|
||||
#endif
|
||||
20
src/Ai/Base/Value/LastSpellCastTimeValue.h
Normal file
20
src/Ai/Base/Value/LastSpellCastTimeValue.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LASTSPELLCASTTIMEVALUE_H
|
||||
#define _PLAYERBOT_LASTSPELLCASTTIMEVALUE_H
|
||||
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class LastSpellCastTimeValue : public ManualSetValue<time_t>, public Qualified
|
||||
{
|
||||
public:
|
||||
LastSpellCastTimeValue(PlayerbotAI* botAI) : ManualSetValue<time_t>(botAI, 0), Qualified() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
24
src/Ai/Base/Value/LastSpellCastValue.cpp
Normal file
24
src/Ai/Base/Value/LastSpellCastValue.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 "LastSpellCastValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
LastSpellCast::LastSpellCast() : id(0), timer(0) {}
|
||||
|
||||
void LastSpellCast::Set(uint32 newId, ObjectGuid newTarget, time_t newTime)
|
||||
{
|
||||
id = newId;
|
||||
target = newTarget;
|
||||
timer = newTime;
|
||||
}
|
||||
|
||||
void LastSpellCast::Reset()
|
||||
{
|
||||
id = 0;
|
||||
target.Clear();
|
||||
timer = 0;
|
||||
}
|
||||
38
src/Ai/Base/Value/LastSpellCastValue.h
Normal file
38
src/Ai/Base/Value/LastSpellCastValue.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LASTSPELLCASTVALUE_H
|
||||
#define _PLAYERBOT_LASTSPELLCASTVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class LastSpellCast
|
||||
{
|
||||
public:
|
||||
LastSpellCast();
|
||||
|
||||
void Set(uint32 id, ObjectGuid target, time_t time);
|
||||
void Reset();
|
||||
|
||||
uint32 id;
|
||||
ObjectGuid target;
|
||||
time_t timer;
|
||||
};
|
||||
|
||||
class LastSpellCastValue : public ManualSetValue<LastSpellCast&>
|
||||
{
|
||||
public:
|
||||
LastSpellCastValue(PlayerbotAI* botAI, std::string const name = "last spell cast")
|
||||
: ManualSetValue<LastSpellCast&>(botAI, data, name)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
LastSpellCast data;
|
||||
};
|
||||
|
||||
#endif
|
||||
34
src/Ai/Base/Value/LeastHpTargetValue.cpp
Normal file
34
src/Ai/Base/Value/LeastHpTargetValue.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "LeastHpTargetValue.h"
|
||||
|
||||
#include "AttackersValue.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class FindLeastHpTargetStrategy : public FindNonCcTargetStrategy
|
||||
{
|
||||
public:
|
||||
FindLeastHpTargetStrategy(PlayerbotAI* botAI) : FindNonCcTargetStrategy(botAI), minHealth(0) {}
|
||||
|
||||
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
|
||||
{
|
||||
Player* bot = botAI->GetBot();
|
||||
if (IsCcTarget(attacker))
|
||||
return;
|
||||
|
||||
if (!result || result->GetHealth() > attacker->GetHealth())
|
||||
result = attacker;
|
||||
}
|
||||
|
||||
protected:
|
||||
float minHealth;
|
||||
};
|
||||
|
||||
Unit* LeastHpTargetValue::Calculate()
|
||||
{
|
||||
FindLeastHpTargetStrategy strategy(botAI);
|
||||
return FindTarget(&strategy);
|
||||
}
|
||||
22
src/Ai/Base/Value/LeastHpTargetValue.h
Normal file
22
src/Ai/Base/Value/LeastHpTargetValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LEASTHPTARGETVALUE_H
|
||||
#define _PLAYERBOT_LEASTHPTARGETVALUE_H
|
||||
|
||||
#include "TargetValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class LeastHpTargetValue : public TargetValue
|
||||
{
|
||||
public:
|
||||
LeastHpTargetValue(PlayerbotAI* botAI, std::string const name = "least hp target") : TargetValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
19
src/Ai/Base/Value/LfgValues.h
Normal file
19
src/Ai/Base/Value/LfgValues.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LFGVALUES_H
|
||||
#define _PLAYERBOT_LFGVALUES_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class LfgProposalValue : public ManualSetValue<uint32>
|
||||
{
|
||||
public:
|
||||
LfgProposalValue(PlayerbotAI* botAI) : ManualSetValue<uint32>(botAI, 0, "lfg proposal") {}
|
||||
};
|
||||
|
||||
#endif
|
||||
35
src/Ai/Base/Value/LineTargetValue.cpp
Normal file
35
src/Ai/Base/Value/LineTargetValue.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "LineTargetValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
Unit* LineTargetValue::Calculate()
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return nullptr;
|
||||
|
||||
Group* group = master->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* prev = master;
|
||||
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
|
||||
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
|
||||
{
|
||||
Player* player = ObjectAccessor::FindPlayer(itr->guid);
|
||||
if (!player || !player->IsAlive() || player == master)
|
||||
continue;
|
||||
|
||||
if (player == bot)
|
||||
return prev;
|
||||
|
||||
prev = player;
|
||||
}
|
||||
|
||||
return master;
|
||||
}
|
||||
22
src/Ai/Base/Value/LineTargetValue.h
Normal file
22
src/Ai/Base/Value/LineTargetValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LINETARGETVALUE_H
|
||||
#define _PLAYERBOT_LINETARGETVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
|
||||
class LineTargetValue : public UnitCalculatedValue
|
||||
{
|
||||
public:
|
||||
LineTargetValue(PlayerbotAI* botAI, std::string const name = "line target") : UnitCalculatedValue(botAI, name) {}
|
||||
|
||||
Unit* Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
22
src/Ai/Base/Value/LogLevelValue.h
Normal file
22
src/Ai/Base/Value/LogLevelValue.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LOGLEVELVALUE_H
|
||||
#define _PLAYERBOT_LOGLEVELVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class LogLevelValue : public ManualSetValue<LogLevel>
|
||||
{
|
||||
public:
|
||||
LogLevelValue(PlayerbotAI* botAI, std::string const name = "log level")
|
||||
: ManualSetValue<LogLevel>(botAI, LOG_LEVEL_DEBUG, name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
98
src/Ai/Base/Value/LootStrategyValue.cpp
Normal file
98
src/Ai/Base/Value/LootStrategyValue.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 "LootStrategyValue.h"
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "ItemUsageValue.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class NormalLootStrategy : public LootStrategy
|
||||
{
|
||||
public:
|
||||
bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override
|
||||
{
|
||||
// Identify the source of loot, loot it if the source is an item in the bots inventory
|
||||
LootObject lootObject = AI_VALUE(LootObject, "loot target");
|
||||
ObjectGuid lootGuid = lootObject.guid;
|
||||
if (lootGuid.IsItem())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, continue with the normal loot logic
|
||||
std::ostringstream out;
|
||||
out << proto->ItemId;
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str());
|
||||
return usage != ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
std::string const GetName() override { return "normal"; }
|
||||
};
|
||||
|
||||
class GrayLootStrategy : public NormalLootStrategy
|
||||
{
|
||||
public:
|
||||
bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override
|
||||
{
|
||||
return NormalLootStrategy::CanLoot(proto, context) || proto->Quality == ITEM_QUALITY_POOR;
|
||||
}
|
||||
|
||||
std::string const GetName() override { return "gray"; }
|
||||
};
|
||||
|
||||
class DisenchantLootStrategy : public NormalLootStrategy
|
||||
{
|
||||
public:
|
||||
bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override
|
||||
{
|
||||
return NormalLootStrategy::CanLoot(proto, context) ||
|
||||
(proto->Quality >= ITEM_QUALITY_UNCOMMON && proto->Bonding != BIND_WHEN_PICKED_UP &&
|
||||
(proto->Class == ITEM_CLASS_ARMOR || proto->Class == ITEM_CLASS_WEAPON));
|
||||
}
|
||||
|
||||
std::string const GetName() override { return "disenchant"; }
|
||||
};
|
||||
|
||||
class AllLootStrategy : public LootStrategy
|
||||
{
|
||||
public:
|
||||
bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override { return true; }
|
||||
|
||||
std::string const GetName() override { return "all"; }
|
||||
};
|
||||
|
||||
LootStrategyValue::~LootStrategyValue()
|
||||
{
|
||||
// delete defaultValue;
|
||||
}
|
||||
|
||||
LootStrategy* LootStrategyValue::normal = new NormalLootStrategy();
|
||||
LootStrategy* LootStrategyValue::gray = new GrayLootStrategy();
|
||||
LootStrategy* LootStrategyValue::disenchant = new DisenchantLootStrategy();
|
||||
LootStrategy* LootStrategyValue::all = new AllLootStrategy();
|
||||
|
||||
LootStrategy* LootStrategyValue::instance(std::string const strategy)
|
||||
{
|
||||
if (strategy == "*" || strategy == "all")
|
||||
return all;
|
||||
|
||||
if (strategy == "g" || strategy == "gray")
|
||||
return gray;
|
||||
|
||||
if (strategy == "d" || strategy == "e" || strategy == "disenchant" || strategy == "enchant")
|
||||
return disenchant;
|
||||
|
||||
return normal;
|
||||
}
|
||||
|
||||
std::string const LootStrategyValue::Save() { return value ? value->GetName() : "?"; }
|
||||
|
||||
bool LootStrategyValue::Load(std::string const text)
|
||||
{
|
||||
value = LootStrategyValue::instance(text);
|
||||
return true;
|
||||
}
|
||||
33
src/Ai/Base/Value/LootStrategyValue.h
Normal file
33
src/Ai/Base/Value/LootStrategyValue.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LOOTSTRATEGYVALUE_H
|
||||
#define _PLAYERBOT_LOOTSTRATEGYVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class LootStrategy;
|
||||
class PlayerbotAI;
|
||||
|
||||
class LootStrategyValue : public ManualSetValue<LootStrategy*>
|
||||
{
|
||||
public:
|
||||
LootStrategyValue(PlayerbotAI* botAI, std::string const name = "loot strategy")
|
||||
: ManualSetValue<LootStrategy*>(botAI, normal, name)
|
||||
{
|
||||
}
|
||||
virtual ~LootStrategyValue();
|
||||
|
||||
std::string const Save() override;
|
||||
bool Load(std::string const value) override;
|
||||
|
||||
static LootStrategy* normal;
|
||||
static LootStrategy* gray;
|
||||
static LootStrategy* all;
|
||||
static LootStrategy* disenchant;
|
||||
static LootStrategy* instance(std::string const name);
|
||||
};
|
||||
|
||||
#endif
|
||||
152
src/Ai/Base/Value/LootValues.cpp
Normal file
152
src/Ai/Base/Value/LootValues.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 "LootValues.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "SharedValueContext.h"
|
||||
|
||||
LootTemplateAccess const* DropMapValue::GetLootTemplate(ObjectGuid guid, LootType type)
|
||||
{
|
||||
LootTemplate const* lTemplate = nullptr;
|
||||
|
||||
if (guid.IsCreature())
|
||||
{
|
||||
CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(guid.GetEntry());
|
||||
|
||||
if (info)
|
||||
{
|
||||
if (type == LOOT_CORPSE)
|
||||
lTemplate = LootTemplates_Creature.GetLootFor(info->lootid);
|
||||
else if (type == LOOT_PICKPOCKETING && info->pickpocketLootId)
|
||||
lTemplate = LootTemplates_Pickpocketing.GetLootFor(info->pickpocketLootId);
|
||||
else if (type == LOOT_SKINNING && info->SkinLootId)
|
||||
lTemplate = LootTemplates_Skinning.GetLootFor(info->SkinLootId);
|
||||
}
|
||||
}
|
||||
else if (guid.IsGameObject())
|
||||
{
|
||||
GameObjectTemplate const* info = sObjectMgr->GetGameObjectTemplate(guid.GetEntry());
|
||||
if (info && info->GetLootId() != 0)
|
||||
{
|
||||
if (type == LOOT_CORPSE)
|
||||
lTemplate = LootTemplates_Gameobject.GetLootFor(info->GetLootId());
|
||||
else if (type == LOOT_FISHINGHOLE)
|
||||
lTemplate = LootTemplates_Fishing.GetLootFor(info->GetLootId());
|
||||
}
|
||||
}
|
||||
else if (guid.IsItem())
|
||||
{
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(guid.GetEntry());
|
||||
|
||||
if (proto)
|
||||
{
|
||||
if (type == LOOT_CORPSE)
|
||||
lTemplate = LootTemplates_Item.GetLootFor(proto->ItemId);
|
||||
else if (type == LOOT_DISENCHANTING && proto->DisenchantID)
|
||||
lTemplate = LootTemplates_Disenchant.GetLootFor(proto->DisenchantID);
|
||||
if (type == LOOT_MILLING)
|
||||
lTemplate = LootTemplates_Milling.GetLootFor(proto->ItemId);
|
||||
if (type == LOOT_PROSPECTING)
|
||||
lTemplate = LootTemplates_Prospecting.GetLootFor(proto->ItemId);
|
||||
}
|
||||
}
|
||||
|
||||
LootTemplateAccess const* lTemplateA = reinterpret_cast<LootTemplateAccess const*>(lTemplate);
|
||||
|
||||
return lTemplateA;
|
||||
}
|
||||
|
||||
DropMap* DropMapValue::Calculate()
|
||||
{
|
||||
DropMap* dropMap = new DropMap;
|
||||
|
||||
int32 sEntry = 0;
|
||||
|
||||
if (CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates())
|
||||
{
|
||||
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||
{
|
||||
sEntry = itr->first;
|
||||
|
||||
if (LootTemplateAccess const* lTemplateA =
|
||||
GetLootTemplate(ObjectGuid::Create<HighGuid::Unit>(sEntry, uint32(1)), LOOT_CORPSE))
|
||||
for (auto const& lItem : lTemplateA->Entries)
|
||||
dropMap->insert(std::make_pair(lItem->itemid, sEntry));
|
||||
}
|
||||
}
|
||||
|
||||
if (GameObjectTemplateContainer const* gameobjects = sObjectMgr->GetGameObjectTemplates())
|
||||
{
|
||||
for (auto const& itr : *gameobjects)
|
||||
{
|
||||
sEntry = itr.first;
|
||||
|
||||
if (LootTemplateAccess const* lTemplateA =
|
||||
GetLootTemplate(ObjectGuid::Create<HighGuid::GameObject>(sEntry, uint32(1)), LOOT_CORPSE))
|
||||
for (auto const& lItem : lTemplateA->Entries)
|
||||
dropMap->insert(std::make_pair(lItem->itemid, -sEntry));
|
||||
}
|
||||
}
|
||||
|
||||
return dropMap;
|
||||
}
|
||||
|
||||
// What items does this entry have in its loot list?
|
||||
std::vector<int32> ItemDropListValue::Calculate()
|
||||
{
|
||||
uint32 itemId = stoi(getQualifier());
|
||||
|
||||
DropMap* dropMap = GAI_VALUE(DropMap*, "drop map");
|
||||
|
||||
std::vector<int32> entries;
|
||||
|
||||
auto range = dropMap->equal_range(itemId);
|
||||
|
||||
for (auto itr = range.first; itr != range.second; ++itr)
|
||||
entries.push_back(itr->second);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
// What items does this entry have in its loot list?
|
||||
std::vector<uint32> EntryLootListValue::Calculate()
|
||||
{
|
||||
int32 entry = stoi(getQualifier());
|
||||
|
||||
std::vector<uint32> items;
|
||||
|
||||
LootTemplateAccess const* lTemplateA;
|
||||
|
||||
if (entry > 0)
|
||||
lTemplateA = DropMapValue::GetLootTemplate(ObjectGuid::Create<HighGuid::Unit>(entry, uint32(1)), LOOT_CORPSE);
|
||||
else
|
||||
lTemplateA =
|
||||
DropMapValue::GetLootTemplate(ObjectGuid::Create<HighGuid::GameObject>(entry, uint32(1)), LOOT_CORPSE);
|
||||
|
||||
if (lTemplateA)
|
||||
for (auto const& lItem : lTemplateA->Entries)
|
||||
items.push_back(lItem->itemid);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
itemUsageMap EntryLootUsageValue::Calculate()
|
||||
{
|
||||
itemUsageMap items;
|
||||
|
||||
for (auto itemId : GAI_VALUE2(std::vector<uint32>, "entry loot list", getQualifier()))
|
||||
{
|
||||
items[AI_VALUE2(ItemUsage, "item usage", itemId)].push_back(itemId);
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
bool HasUpgradeValue::Calculate()
|
||||
{
|
||||
itemUsageMap uMap = AI_VALUE2(itemUsageMap, "entry loot usage", getQualifier());
|
||||
return uMap.find(ITEM_USAGE_EQUIP) != uMap.end() || uMap.find(ITEM_USAGE_REPLACE) != uMap.end();
|
||||
}
|
||||
76
src/Ai/Base/Value/LootValues.h
Normal file
76
src/Ai/Base/Value/LootValues.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_LOOTVALUES_H
|
||||
#define _PLAYERBOT_LOOTVALUES_H
|
||||
|
||||
#include "ItemUsageValue.h"
|
||||
#include "LootMgr.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
// Cheat class copy to hack into the loot system
|
||||
class LootTemplateAccess
|
||||
{
|
||||
public:
|
||||
class LootGroup; // A set of loot definitions for items (refs are not allowed inside)
|
||||
typedef std::vector<LootGroup> LootGroups;
|
||||
LootStoreItemList Entries; // not grouped only
|
||||
LootGroups Groups; // groups have own (optimized) processing, grouped entries go there
|
||||
};
|
||||
|
||||
// itemId, entry
|
||||
typedef std::unordered_map<uint32, int32> DropMap;
|
||||
|
||||
// Returns the loot map of all entries
|
||||
class DropMapValue : public SingleCalculatedValue<DropMap*>
|
||||
{
|
||||
public:
|
||||
DropMapValue(PlayerbotAI* botAI) : SingleCalculatedValue(botAI, "drop map") {}
|
||||
|
||||
static LootTemplateAccess const* GetLootTemplate(ObjectGuid guid, LootType type = LOOT_CORPSE);
|
||||
|
||||
DropMap* Calculate() override;
|
||||
};
|
||||
|
||||
// Returns the entries that drop a specific item
|
||||
class ItemDropListValue : public SingleCalculatedValue<std::vector<int32>>, public Qualified
|
||||
{
|
||||
public:
|
||||
ItemDropListValue(PlayerbotAI* botAI) : SingleCalculatedValue(botAI, "item drop list") {}
|
||||
|
||||
std::vector<int32> Calculate() override;
|
||||
};
|
||||
|
||||
// Returns the items a specific entry can drop
|
||||
class EntryLootListValue : public SingleCalculatedValue<std::vector<uint32>>, public Qualified
|
||||
{
|
||||
public:
|
||||
EntryLootListValue(PlayerbotAI* botAI) : SingleCalculatedValue(botAI, "entry loot list") {}
|
||||
|
||||
std::vector<uint32> Calculate() override;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<ItemUsage, std::vector<uint32>> itemUsageMap;
|
||||
|
||||
class EntryLootUsageValue : public CalculatedValue<itemUsageMap>, public Qualified
|
||||
{
|
||||
public:
|
||||
EntryLootUsageValue(PlayerbotAI* botAI) : CalculatedValue(botAI, "entry loot usage", 2 * 1000) {}
|
||||
|
||||
itemUsageMap Calculate() override;
|
||||
};
|
||||
|
||||
class HasUpgradeValue : public BoolCalculatedValue, public Qualified
|
||||
{
|
||||
public:
|
||||
HasUpgradeValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "has upgrade", 2 * 1000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
60
src/Ai/Base/Value/MaintenanceValues.cpp
Normal file
60
src/Ai/Base/Value/MaintenanceValues.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 "MaintenanceValues.h"
|
||||
|
||||
#include "BudgetValues.h"
|
||||
#include "ItemUsageValue.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool CanMoveAroundValue::Calculate()
|
||||
{
|
||||
if (bot->IsInCombat())
|
||||
return false;
|
||||
|
||||
if (bot->GetTradeData())
|
||||
return false;
|
||||
|
||||
if (botAI->HasStrategy("stay", BOT_STATE_NON_COMBAT))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE(bool, "group ready"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShouldHomeBindValue::Calculate() { return AI_VALUE2(float, "distance", "home bind") > 1000.0f; }
|
||||
|
||||
bool ShouldRepairValue::Calculate() { return AI_VALUE(uint8, "durability") < 80; }
|
||||
|
||||
bool CanRepairValue::Calculate()
|
||||
{
|
||||
return AI_VALUE(uint8, "durability") < 100 &&
|
||||
AI_VALUE(uint32, "repair cost") < AI_VALUE2(uint32, "free money for", (uint32)NeedMoneyFor::repair);
|
||||
}
|
||||
|
||||
bool ShouldSellValue::Calculate() { return AI_VALUE(uint8, "bag space") > 80; }
|
||||
|
||||
bool CanSellValue::Calculate()
|
||||
{
|
||||
return (AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_VENDOR)) +
|
||||
AI_VALUE2(uint32, "item count", "usage " + std::to_string(ITEM_USAGE_AH))) > 1;
|
||||
}
|
||||
|
||||
bool CanFightEqualValue::Calculate() { return AI_VALUE(uint8, "durability") > 20; }
|
||||
|
||||
bool CanFightEliteValue::Calculate()
|
||||
{
|
||||
return bot->GetGroup() && AI_VALUE2(bool, "group and", "can fight equal") &&
|
||||
AI_VALUE2(bool, "group and", "following party") && !AI_VALUE2(bool, "group or", "should sell,can sell");
|
||||
}
|
||||
|
||||
bool CanFightBossValue::Calculate()
|
||||
{
|
||||
return bot->GetGroup() && bot->GetGroup()->GetMembersCount() > 3 &&
|
||||
AI_VALUE2(bool, "group and", "can fight equal") && AI_VALUE2(bool, "group and", "following party") &&
|
||||
!AI_VALUE2(bool, "group or", "should sell,can sell");
|
||||
}
|
||||
85
src/Ai/Base/Value/MaintenanceValues.h
Normal file
85
src/Ai/Base/Value/MaintenanceValues.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_MAINTANCEVALUE_H
|
||||
#define _PLAYERBOT_MAINTANCEVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class CanMoveAroundValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanMoveAroundValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can move around", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class ShouldHomeBindValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
ShouldHomeBindValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "should home bind", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class ShouldRepairValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
ShouldRepairValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "should repair", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanRepairValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanRepairValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can repair", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class ShouldSellValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
ShouldSellValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "should sell", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanSellValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanSellValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can sell", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanFightEqualValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanFightEqualValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fight equal", 2 * 2000) {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanFightEliteValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanFightEliteValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fight elite") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
class CanFightBossValue : public BoolCalculatedValue
|
||||
{
|
||||
public:
|
||||
CanFightBossValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fight boss") {}
|
||||
|
||||
bool Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/Ai/Base/Value/ManaSaveLevelValue.h
Normal file
32
src/Ai/Base/Value/ManaSaveLevelValue.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_MANASAVELEVELVALUE_H
|
||||
#define _PLAYERBOT_MANASAVELEVELVALUE_H
|
||||
|
||||
#include "Value.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class ManaSaveLevelValue : public ManualSetValue<double>
|
||||
{
|
||||
public:
|
||||
ManaSaveLevelValue(PlayerbotAI* botAI) : ManualSetValue<double>(botAI, 1.0, "mana save level") {}
|
||||
|
||||
std::string const Save()
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << value;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
bool Load(std::string const text)
|
||||
{
|
||||
value = atof(text.c_str());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
14
src/Ai/Base/Value/NearestAdsValue.cpp
Normal file
14
src/Ai/Base/Value/NearestAdsValue.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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 "NearestAdsValue.h"
|
||||
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool NearestAddsValue::AcceptUnit(Unit* unit)
|
||||
{
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
return unit != target;
|
||||
}
|
||||
26
src/Ai/Base/Value/NearestAdsValue.h
Normal file
26
src/Ai/Base/Value/NearestAdsValue.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_NEARESTADSVALUE_H
|
||||
#define _PLAYERBOT_NEARESTADSVALUE_H
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PossibleTargetsValue.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class NearestAddsValue : public PossibleTargetsValue
|
||||
{
|
||||
public:
|
||||
NearestAddsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->tooCloseDistance)
|
||||
: PossibleTargetsValue(botAI, "nearest adds", range, true)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool AcceptUnit(Unit* unit) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
32
src/Ai/Base/Value/NearestCorpsesValue.cpp
Normal file
32
src/Ai/Base/Value/NearestCorpsesValue.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 "NearestCorpsesValue.h"
|
||||
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
class AnyDeadUnitInObjectRangeCheck
|
||||
{
|
||||
public:
|
||||
AnyDeadUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
|
||||
WorldObject const& GetFocusObject() const { return *i_obj; }
|
||||
bool operator()(Unit* u) { return !u->IsAlive(); }
|
||||
|
||||
private:
|
||||
WorldObject const* i_obj;
|
||||
float i_range;
|
||||
};
|
||||
|
||||
void NearestCorpsesValue::FindUnits(std::list<Unit*>& targets)
|
||||
{
|
||||
AnyDeadUnitInObjectRangeCheck u_check(bot, range);
|
||||
Acore::UnitListSearcher<AnyDeadUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||
Cell::VisitObjects(bot, searcher, range);
|
||||
}
|
||||
|
||||
bool NearestCorpsesValue::AcceptUnit(Unit* unit) { return true; }
|
||||
27
src/Ai/Base/Value/NearestCorpsesValue.h
Normal file
27
src/Ai/Base/Value/NearestCorpsesValue.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_NEARESTCORPSESVALUE_H
|
||||
#define _PLAYERBOT_NEARESTCORPSESVALUE_H
|
||||
|
||||
#include "NearestUnitsValue.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class NearestCorpsesValue : public NearestUnitsValue
|
||||
{
|
||||
public:
|
||||
NearestCorpsesValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance)
|
||||
: NearestUnitsValue(botAI, "nearest corpses", range, true)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void FindUnits(std::list<Unit*>& targets) override;
|
||||
bool AcceptUnit(Unit* unit) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
24
src/Ai/Base/Value/NearestFriendlyPlayersValue.cpp
Normal file
24
src/Ai/Base/Value/NearestFriendlyPlayersValue.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 "NearestFriendlyPlayersValue.h"
|
||||
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
void NearestFriendlyPlayersValue::FindUnits(std::list<Unit*>& targets)
|
||||
{
|
||||
Acore::AnyFriendlyUnitInObjectRangeCheck u_check(bot, bot, range);
|
||||
Acore::UnitListSearcher<Acore::AnyFriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||
Cell::VisitObjects(bot, searcher, range);
|
||||
}
|
||||
|
||||
bool NearestFriendlyPlayersValue::AcceptUnit(Unit* unit)
|
||||
{
|
||||
ObjectGuid guid = unit->GetGUID();
|
||||
return guid.IsPlayer() && guid != botAI->GetBot()->GetGUID();
|
||||
}
|
||||
27
src/Ai/Base/Value/NearestFriendlyPlayersValue.h
Normal file
27
src/Ai/Base/Value/NearestFriendlyPlayersValue.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_NEARESTFRIENDLYPLAYERSVALUES_H
|
||||
#define _PLAYERBOT_NEARESTFRIENDLYPLAYERSVALUES_H
|
||||
|
||||
#include "NearestUnitsValue.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class NearestFriendlyPlayersValue : public NearestUnitsValue
|
||||
{
|
||||
public:
|
||||
NearestFriendlyPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance)
|
||||
: NearestUnitsValue(botAI, "nearest friendly players", range)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void FindUnits(std::list<Unit*>& targets) override;
|
||||
bool AcceptUnit(Unit* unit) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
84
src/Ai/Base/Value/NearestGameObjects.cpp
Normal file
84
src/Ai/Base/Value/NearestGameObjects.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 "NearestGameObjects.h"
|
||||
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SpellMgr.h"
|
||||
|
||||
GuidVector NearestGameObjects::Calculate()
|
||||
{
|
||||
std::list<GameObject*> targets;
|
||||
AnyGameObjectInObjectRangeCheck u_check(bot, range);
|
||||
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||
Cell::VisitObjects(bot, searcher, range);
|
||||
|
||||
GuidVector result;
|
||||
for (GameObject* go : targets)
|
||||
{
|
||||
// if (ignoreLos || bot->IsWithinLOSInMap(go))
|
||||
result.push_back(go->GetGUID());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
GuidVector NearestTrapWithDamageValue::Calculate()
|
||||
{
|
||||
std::list<GameObject*> targets;
|
||||
AnyGameObjectInObjectRangeCheck u_check(bot, range);
|
||||
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||
Cell::VisitObjects(bot, searcher, range);
|
||||
|
||||
GuidVector result;
|
||||
for (GameObject* go : targets)
|
||||
{
|
||||
if (go->GetGoType() != GAMEOBJECT_TYPE_TRAP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Unit* owner = go->GetOwner();
|
||||
if (owner && owner->IsFriendlyTo(bot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const GameObjectTemplate* goInfo = go->GetGOInfo();
|
||||
if (!goInfo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uint32 spellId = goInfo->trap.spellId;
|
||||
if (!spellId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo || spellInfo->IsPositive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < MAX_SPELL_EFFECTS; i++)
|
||||
{
|
||||
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA)
|
||||
{
|
||||
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE)
|
||||
{
|
||||
result.push_back(go->GetGUID());
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE)
|
||||
{
|
||||
result.push_back(go->GetGUID());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
65
src/Ai/Base/Value/NearestGameObjects.h
Normal file
65
src/Ai/Base/Value/NearestGameObjects.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_NEARESTGAMEOBJECTS_H
|
||||
#define _PLAYERBOT_NEARESTGAMEOBJECTS_H
|
||||
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "Value.h"
|
||||
#include "GameObject.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class AnyGameObjectInObjectRangeCheck
|
||||
{
|
||||
public:
|
||||
AnyGameObjectInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {}
|
||||
WorldObject const& GetFocusObject() const { return *i_obj; }
|
||||
bool operator()(GameObject* u)
|
||||
{
|
||||
if (u && i_obj->IsWithinDistInMap(u, i_range) && u->isSpawned() && u->GetGOInfo())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
WorldObject const* i_obj;
|
||||
float i_range;
|
||||
};
|
||||
|
||||
class NearestGameObjects : public ObjectGuidListCalculatedValue
|
||||
{
|
||||
public:
|
||||
NearestGameObjects(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = false,
|
||||
std::string const name = "nearest game objects")
|
||||
: ObjectGuidListCalculatedValue(botAI, name, 1 * 1000), range(range), ignoreLos(ignoreLos)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GuidVector Calculate() override;
|
||||
|
||||
private:
|
||||
float range;
|
||||
bool ignoreLos;
|
||||
};
|
||||
|
||||
class NearestTrapWithDamageValue : public ObjectGuidListCalculatedValue
|
||||
{
|
||||
public:
|
||||
NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = 15.0f)
|
||||
: ObjectGuidListCalculatedValue(botAI, "nearest trap with damage", 1 * 1000), range(range)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
GuidVector Calculate() override;
|
||||
|
||||
private:
|
||||
float range;
|
||||
};
|
||||
|
||||
#endif
|
||||
25
src/Ai/Base/Value/NearestNonBotPlayersValue.cpp
Normal file
25
src/Ai/Base/Value/NearestNonBotPlayersValue.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 "NearestNonBotPlayersValue.h"
|
||||
|
||||
#include "CellImpl.h"
|
||||
#include "GridNotifiers.h"
|
||||
#include "GridNotifiersImpl.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
void NearestNonBotPlayersValue::FindUnits(std::list<Unit*>& targets)
|
||||
{
|
||||
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
|
||||
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||
Cell::VisitObjects(bot, searcher, range);
|
||||
}
|
||||
|
||||
bool NearestNonBotPlayersValue::AcceptUnit(Unit* unit)
|
||||
{
|
||||
ObjectGuid guid = unit->GetGUID();
|
||||
return guid.IsPlayer() && !GET_PLAYERBOT_AI(((Player*)unit)) &&
|
||||
(!((Player*)unit)->IsGameMaster() || ((Player*)unit)->isGMVisible());
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user