Big update.

This commit is contained in:
UltraNix
2022-03-12 22:27:09 +01:00
parent b3d00ccb26
commit b952636f0d
843 changed files with 1534330 additions and 99 deletions

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AoeHealValues.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;
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;
float percent = (static_cast<float> (player->GetHealth()) / player->GetMaxHealth()) * 100;
if (percent <= range)
++count;
}
return count;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AoeValues.h"
#include "Playerbots.h"
#include "ServerFacade.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();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_AOEVALUES_H
#define _PLAYERBOT_AOEVALUES_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;
};
#endif

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Arrow.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();
float orientation = master->GetOrientation();
MultiLineUnitPlacer placer(orientation);
tanks.PlaceUnits(&placer);
tanks.Move(-cos(orientation) * offset, -sin(orientation) * offset);
offset += tankLines * sPlayerbotAIConfig->followDistance;
melee.PlaceUnits(&placer);
melee.Move(-cos(orientation) * offset, -sin(orientation) * offset);
offset += meleeLines * sPlayerbotAIConfig->followDistance;
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);
float x = master->GetPositionX() - masterUnit->GetX() + botUnit->GetX();
float y = master->GetPositionY() - masterUnit->GetY() + botUnit->GetY();
float z = master->GetPositionZ();
float ground = master->GetMap()->GetHeight(x, y, z + 0.5f);
if (ground <= INVALID_HEIGHT)
return Formation::NullLocation;
return WorldLocation(master->GetMapId(), x, y, 0.05f + ground);
}
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 (member == bot)
FindSlot(member)->AddLast(botUnit = new FormationUnit(index, false));
else if (member != botAI->GetMaster())
FindSlot(member)->AddLast(new FormationUnit(index, false));
gref = gref->next();
++index;
}
}
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();
}

119
src/strategy/values/Arrow.h Normal file
View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AttackerCountValues.h"
#include "Playerbots.h"
uint8 MyAttackerCountValue::Calculate()
{
return bot->getAttackers().size();
}
bool HasAggroValue::Calculate()
{
Unit* target = GetTarget();
if (!target)
return true;
HostileReference *ref = bot->getHostileRefMgr().getFirst();
if (!ref)
return true; // simulate as target is not atacking anybody yet
while( ref )
{
ThreatMgr* threatMgr = ref->GetSource();
Unit* attacker = threatMgr->GetOwner();
Unit* victim = attacker->GetVictim();
if (victim == bot && target == attacker)
return true;
ref = ref->next();
}
ref = target->getThreatMgr().getCurrentVictim();
if (ref)
{
if (Unit* victim = ref->getTarget())
{
if (Player* pl = victim->ToPlayer())
{
if (botAI->IsTank(pl))
{
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();
}
}
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 *= 5;
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();
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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 agro") : 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

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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();
for (ObjectGuid const guid : attackers)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || unit == target)
continue;
if (bot->GetDistance(unit) > botAI->GetRange("spell"))
continue;
if (!botAI->HasAura(qualifier, unit))
return unit;
}
return nullptr;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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) : UnitCalculatedValue(botAI, "attacker without aura") { }
protected:
Unit* Calculate() override;
};
#endif

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AttackersValue.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Playerbots.h"
#include "ServerFacade.h"
GuidVector AttackersValue::Calculate()
{
std::set<Unit*> targets;
GuidVector result;
if (!botAI->AllowActivity(ALL_ACTIVITY))
return std::move(result);
AddAttackersOf(bot, targets);
if (Group* group = bot->GetGroup())
AddAttackersOf(group, targets);
RemoveNonThreating(targets);
for (Unit* unit : targets)
result.push_back(unit->GetGUID());
if (bot->duel && bot->duel->Opponent)
result.push_back(bot->duel->Opponent->GetGUID());
return std::move(result);
}
void AttackersValue::AddAttackersOf(Group* group, std::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())
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::set<Unit*>& targets)
{
if (!player || !player->IsInWorld() || player->IsBeingTeleported())
return;
std::list<Unit*> units;
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(player, player, sPlayerbotAIConfig->sightDistance);
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(player, units, u_check);
Cell::VisitAllObjects(player, searcher, sPlayerbotAIConfig->sightDistance);
for (Unit* unit : units)
{
if (!player->GetGroup())
{
if (!unit->getThreatMgr().getThreat(player) && (!unit->getThreatMgr().getCurrentVictim() || unit->getThreatMgr().getCurrentVictim()->getTarget() != player))
continue;
}
targets.insert(unit);
}
}
void AttackersValue::RemoveNonThreating(std::set<Unit*>& targets)
{
for (std::set<Unit*>::iterator tIter = targets.begin(); tIter != targets.end();)
{
Unit* unit = *tIter;
if (!IsValidTarget(unit, bot))
{
std::set<Unit*>::iterator tIter2 = tIter;
++tIter;
targets.erase(tIter2);
}
else
++tIter;
}
}
bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
{
Creature* c = attacker->ToCreature();
bool rti = false;
if (attacker && bot->GetGroup())
rti = bot->GetGroup()->GetTargetIcon(7) == attacker->GetGUID();
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
bool leaderHasThreat = false;
if (attacker && 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 inCannon = botAI->IsInVehicle(false, true);
return attacker && attacker->IsInWorld() && attacker->GetMapId() == bot->GetMapId() && !attacker->isDead() && !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) &&
(inCannon || !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) && attacker->CanSeeOrDetect(bot) &&
!(attacker->HasUnitState(UNIT_STATE_STUNNED) && botAI->HasAura("shackle undead", attacker)) && !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/ attacker->isFeared()) && !rti) &&
/*!sServerFacade->IsInRoots(attacker) &&*/!attacker->IsFriendlyTo(bot) && bot->IsWithinDistInMap(attacker, range) &&
!(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) && !(sPlayerbotAIConfig->IsInPvpProhibitedZone(attacker->GetAreaId()) &&
(attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet())) && (!c || (!c->IsInEvadeMode() && ((!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))));
}
bool AttackersValue::IsValidTarget(Unit *attacker, Player *bot)
{
return IsPossibleTarget(attacker, bot) && (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;
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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", 2) { }
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::set<Unit*>& targets);
void AddAttackersOf(Player* player, std::set<Unit*>& targets);
void RemoveNonThreating(std::set<Unit*>& targets);
};
class PossibleAddsValue : public BoolCalculatedValue
{
public:
PossibleAddsValue(PlayerbotAI* botAI, std::string const name = "possible adds") : BoolCalculatedValue(botAI, name) { }
bool Calculate() override;
};
#endif

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,229 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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)
{
if (itr->second.trainer_type != TRAINER_TYPE_CLASS && itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
continue;
if (itr->second.trainer_type == TRAINER_TYPE_CLASS && itr->second.trainer_class != bot->getClass())
continue;
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(itr->first);
if (!trainer_spells)
continue;
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin(); iter != trainer_spells->spellList.end(); ++iter)
{
TrainerSpell const* tSpell = &iter->second;
if (!tSpell)
continue;
TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
if (state != TRAINER_SPELL_GREEN)
continue;
if (itr->second.trainer_type == TRAINER_TYPE_TRADESKILLS)
continue;
if (spells.find(tSpell->spell) != spells.end())
continue;
TotalCost += tSpell->spellCost;
spells.insert(tSpell->spell);
}
}
}
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;
}
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);
};

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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::VisitAllObjects(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;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CRAFTVALUE_H
#define _PLAYERBOT_CRAFTVALUE_H
#include "Value.h"
#include <map>
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

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_CURRENTCCTARGETVALUE_H
#define _PLAYERBOT_CURRENTCCTARGETVALUE_H
#include "TargetValue.h"
#include "NamedObjectContext.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

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DistanceValue.h"
#include "Formations.h"
#include "PositionValue.h"
#include "Stances.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
#include "ServerFacade.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)
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")
{
Stance* stance = AI_VALUE(Stance*, "stance");
WorldLocation loc = stance->GetLocation();
return sServerFacade->GetDistance2d(botAI->GetBot(), loc.GetPositionX(), loc.GetPositionY());
}
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());
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DpsTargetValue.h"
#include "Playerbots.h"
class FindLeastHpTargetStrategy : public FindTargetStrategy
{
public:
FindLeastHpTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), minHealth(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 minHealth;
};
Unit* DpsTargetValue::Calculate()
{
Unit* rti = RtiTargetValue::Calculate();
if (rti)
return rti;
FindLeastHpTargetStrategy strategy(botAI);
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);
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,11 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "DuelTargetValue.h"
#include "Playerbots.h"
Unit* DuelTargetValue::Calculate()
{
return bot->duel ? bot->duel->Opponent : nullptr;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,146 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "EnemyPlayerValue.h"
#include "Playerbots.h"
#include "ServerFacade.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->IsInPvpProhibitedZone(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->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)))
return true;
return false;
}
Unit* EnemyPlayerValue::Calculate()
{
bool inCannon = botAI->IsInVehicle(false, 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 (const auto& 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.
uint32 const aggroDistance = (inCannon || bot->GetHealth() > pTarget->GetHealth()) ? maxAggroDistance : 20.0f;
if (!bot->IsWithinDist(pTarget, aggroDistance))
continue;
if (bot->IsWithinLOSInMap(pTarget) && (inCannon || (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 (bot->IsWithinDist(pAttacker, maxAggroDistance * 2.0f) && bot->IsWithinLOSInMap(pAttacker) && pAttacker != pVictim
&& pAttacker->CanSeeOrDetect(bot) && pAttacker->IsPlayer())
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;
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ENEMYPLAYERVALUE_H
#define _PLAYERBOT_ENEMYPLAYERVALUE_H
#include "TargetValue.h"
#include "PossibleTargetsValue.h"
class PlayerbotAI;
class Unit;
class NearestEnemyPlayersValue : public PossibleTargetsValue
{
public:
NearestEnemyPlayersValue(PlayerbotAI* botAI, float range = 120.0f) :
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) { }
Unit* Calculate() override;
private:
float GetMaxAttackDistance();
};
#endif

View File

@@ -0,0 +1,629 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "Formations.h"
#include "Arrow.h"
#include "Event.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);
}
WorldLocation MoveAheadFormation::GetLocation()
{
Player* master = botAI->GetGroupMaster();
if (!master || master == bot)
return WorldLocation();
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 = master->GetMap()->GetHeight(x1, y1, z);
if (ground > INVALID_HEIGHT)
{
x = x1;
y = y1;
}
}
float ground = master->GetMap()->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 "master target"; }
};
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();
if (!master)
return WorldLocation();
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();
float ground = master->GetMap()->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);
}
float GetMaxDistance() override { return sPlayerbotAIConfig->followDistance; }
};
class ChaosFormation : public MoveAheadFormation
{
public:
ChaosFormation(PlayerbotAI* botAI) : MoveAheadFormation(botAI, "chaos"), lastChangeTime(0) { }
WorldLocation GetLocationInternal() override
{
Player* master = botAI->GetGroupMaster();
if (!master)
return WorldLocation();
float range = sPlayerbotAIConfig->followDistance;
float angle = GetFollowAngle();
time_t now = time(nullptr);
if (!lastChangeTime || now - lastChangeTime >= 3)
{
lastChangeTime = now;
dx = (urand(0, 10) / 10.0 - 0.5) * sPlayerbotAIConfig->tooCloseDistance;
dy = (urand(0, 10) / 10.0 - 0.5) * sPlayerbotAIConfig->tooCloseDistance;
dr = sqrt(dx*dx + dy*dy);
}
float x = master->GetPositionX() + cos(angle) * range + dx;
float y = master->GetPositionY() + sin(angle) * range + dy;
float z = master->GetPositionZ();
float ground = master->GetMap()->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);
}
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
{
float range = 2.0f;
Unit* target = AI_VALUE(Unit*, "current target");
Player* master = botAI->GetGroupMaster();
if (!target && target != bot)
target = master;
if (!target)
return Formation::NullLocation;
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();
float ground = target->GetMap()->GetHeight(x, y, z);
if (ground <= INVALID_HEIGHT)
return Formation::NullLocation;
z += CONTACT_DISTANCE;
bot->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 = botAI->GetGroupMaster();
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;
GroupReference* gref = group->GetFirstMember();
while (gref)
{
Player* member = gref->GetSource();
if (member != master)
players.push_back(member);
gref = gref->next();
}
players.insert(players.begin() + group->GetMembersCount() / 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;
GroupReference* gref = group->GetFirstMember();
while (gref)
{
Player* member = gref->GetSource();
if (member != master)
{
if (botAI->IsTank(member))
tanks.push_back(member);
else
dps.push_back(member);
}
gref = gref->next();
}
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
{
float range = sPlayerbotAIConfig->farDistance;
float followRange = sPlayerbotAIConfig->followDistance;
Player* master = GetMaster();
if (!master)
return Formation::NullLocation;
if (sServerFacade->GetDistance2d(bot, master) <= range)
return Formation::NullLocation;
float angle = master->GetAngle(bot);
float followAngle = GetFollowAngle();
float x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
float y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
float z = master->GetPositionZ();
float ground = master->GetMap()->GetHeight(x, y, z);
if (ground <= INVALID_HEIGHT)
{
float minDist = 0, minX = 0, minY = 0;
for (float angle = 0.0f; angle <= 2 * M_PI; angle += M_PI / 16.0f)
{
x = master->GetPositionX() + cos(angle) * range + cos(followAngle) * followRange;
y = master->GetPositionY() + sin(angle) * range + sin(followAngle) * followRange;
float dist = sServerFacade->GetDistance2d(bot, x, y);
float ground = master->GetMap()->GetHeight(x, y, z);
if (ground > INVALID_HEIGHT && (!minDist || minDist > dist))
{
minDist = dist;
minX = x;
minY = y;
}
}
if (minDist)
{
z += CONTACT_DISTANCE;
bot->UpdateAllowedPositionZ(minX, minY, z);
return WorldLocation(bot->GetMapId(), minX, minY, z);
}
return Formation::NullLocation;
}
z += CONTACT_DISTANCE;
bot->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);
uint32 index = 1;
uint32 total = 1;
PlayerbotMgr* masterBotMgr = nullptr;
if (master)
masterBotMgr = GET_PLAYERBOT_MGR(master);
if (!group && master && !GET_PLAYERBOT_AI(master) && masterBotMgr)
{
for (PlayerBotMap::const_iterator i = masterBotMgr->GetPlayerBotsBegin(); i != masterBotMgr->GetPlayerBotsEnd(); ++i)
{
if (i->second == bot)
index = total;
++total;
}
}
else if (group)
{
std::vector<Player*> roster;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
if (Player* member = ref->GetSource())
{
if (member != master && !botAI->IsTank(member) && !botAI->IsHeal(member))
{
roster.insert(roster.begin() + roster.size() / 2, member);
}
}
}
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
if (Player* member = ref->GetSource())
{
if (member != master && botAI->IsHeal(member))
{
roster.insert(roster.begin() + roster.size() / 2, member);
}
}
}
bool left = true;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
if (Player* member = ref->GetSource())
{
if (member != master && botAI->IsTank(member))
{
if (left)
roster.push_back(member);
else
roster.insert(roster.begin(), member);
left = !left;
}
}
}
for (Player* playerRoster : roster)
{
if (playerRoster == bot)
break;
++index;
}
total = roster.size() + 1;
}
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 NearFormation(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")
{
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" || formation == "default")
{
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 near (default), queue, chaos, 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 angle = orientation - M_PI / 2.0f;
float x = cx + cos(angle) * (range * floor(count / 2.0f) + diff);
float y = cy + sin(angle) * (range * floor(count / 2.0f) + diff);
uint32 index = 0;
for (Player* member : line)
{
if (member == bot)
{
float angle = orientation + M_PI / 2.0f;
float radius = range * index;
float lx = x + cos(angle) * radius;
float ly = y + sin(angle) * radius;
float lz = cz;
float ground = bot->GetMap()->GetHeight(lx, ly, lz);
if (ground <= INVALID_HEIGHT)
return Formation::NullLocation;
lz += CONTACT_DISTANCE;
bot->UpdateAllowedPositionZ(lx, ly, lz);
return WorldLocation(bot->GetMapId(), lx, ly, lz);
}
++index;
}
return Formation::NullLocation;
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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 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

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "GrindTargetValue.h"
#include "Playerbots.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();
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;
if (abs(bot->GetPositionZ() - unit->GetPositionZ()) > sPlayerbotAIConfig->spellDistance)
continue;
if (!bot->InBattleground() && GetTargetingPlayerCount(unit) > assistCount)
continue;
//if (!bot->InBattleground() && master && master->GetDistance(unit) >= sPlayerbotAIConfig->grindDistance && !sRandomPlayerbotMgr->IsRandomBot(bot))
//continue;
if (!bot->InBattleground() && (int)unit->getLevel() - (int)bot->getLevel() > 4 && !unit->GetGUID().IsPlayer())
continue;
if (needForQuestMap.find(unit->GetEntry()) == needForQuestMap.end())
needForQuestMap[unit->GetEntry()] = needForQuest(unit);
if (!needForQuestMap[unit->GetEntry()])
if ((urand(0, 100) < 75 || (context->GetValue<TravelTarget*>("travel target")->Get()->isWorking() &&
context->GetValue<TravelTarget*>("travel target")->Get()->getDestination()->getName() != "GrindTravelDestination")))
continue;
//if (bot->InBattleground() && bot->GetDistance(unit) > 40.0f)
//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 (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 && urand(0, abs(distance - newdistance)) > sPlayerbotAIConfig->sightDistance * 0.1))
{
distance = newdistance;
result = unit;
}
}
}
return result;
}
bool GrindTargetValue::needForQuest(Unit* target)
{
bool justCheck = (bot->GetGUID() == target->GetGUID());
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_COMPLETE && !bot->GetQuestRewardStatus(questId)))
{
if (!justCheck && !target->hasInvolvedQuest(questId))
continue;
return true;
}
else if (status == QUEST_STATUS_INCOMPLETE)
{
QuestStatusData* questStatus = sTravelMgr->getQuestStatus(bot, questId);
if (questTemplate->GetQuestLevel() > bot->getLevel())
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 || justCheck))
return true;
}
if (justCheck)
{
int32 itemId = questTemplate->RequiredItemId[j];
if (itemId && itemId > 0)
{
uint32 required = questTemplate->RequiredItemCount[j];
uint32 available = questStatus->ItemCount[j];
if (required && available < required)
return true;
}
}
}
if (!justCheck)
{
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;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,171 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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->GetGroupMaster() == bot)
return true;
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
return true;
return false;
};
bool IsNearLeaderValue::Calculate()
{
Player* groupMaster = botAI->GetGroupMaster();
if (!groupMaster)
return false;
if (groupMaster == bot)
return true;
return sServerFacade->GetDistance2d(bot, botAI->GetGroupMaster()) < 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->GetGroupMaster() && sServerFacade->GetDistance2d(member, botAI->GetGroupMaster()) > 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;
};

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NamedObjectContext.h"
#include "Value.h"
class PlayerbotAI;
class GroupMembersValue : public ObjectGuidListCalculatedValue
{
public:
GroupMembersValue(PlayerbotAI* botAI) : ObjectGuidListCalculatedValue(botAI, "group members", 2) { }
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) { }
bool Calculate()override;
};

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,12 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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);
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "HasTotemValue.h"
#include "Playerbots.h"
char* strstri(char const* str1, char const* str2);
bool HasTotemValue::Calculate()
{
GuidVector units = *context->GetValue<GuidVector>("nearest npcs");
for (ObjectGuid const guid : units)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit)
continue;
Creature* creature = dynamic_cast<Creature*>(unit);
if (!creature || !creature->IsTotem())
continue;
if (strstri(creature->GetName().c_str(), qualifier.c_str()) && bot->GetDistance(creature) <= botAI->GetRange("spell"))
return true;
}
return false;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "InvalidTargetValue.h"
#include "AttackersValue.h"
#include "Playerbots.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 !AttackersValue::IsValidTarget(target, bot);
}
return !target;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "IsBehindValue.h"
#include "Playerbots.h"
bool IsBehindValue::Calculate()
{
Unit* target = AI_VALUE(Unit*, qualifier);
if (!target)
return false;
float targetOrientation = target->GetOrientation();
float orientation = bot->GetOrientation();
float distance = bot->GetDistance(target);
return distance <= ATTACK_DISTANCE && abs(targetOrientation - orientation) < M_PI / 2;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,15 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "IsFacingValue.h"
#include "Playerbots.h"
bool IsFacingValue::Calculate()
{
Unit* target = AI_VALUE(Unit*, qualifier);
if (!target)
return false;
return bot->HasInArc(CAST_ANGLE_IN_FRONT, target, sPlayerbotAIConfig->sightDistance);
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
return target->IsUnderWater() || target->IsInWater();
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_ITEMCOUNTVALUE_H
#define _PLAYERBOT_ITEMCOUNTVALUE_H
#include "Item.h"
#include "NamedObjectContext.h"
#include "InventoryAction.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

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
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;
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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) { }
Item* Calculate() override;
private:
Item* GetItemFitsToSpellRequirements(uint8 slot, SpellInfo const *spellInfo);
};
#endif

View File

@@ -0,0 +1,638 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "ItemUsageValue.h"
#include "ChatHelper.h"
#include "GuildTaskMgr.h"
#include "Playerbots.h"
#include "RandomItemMgr.h"
#include "ServerFacade.h"
ItemUsage ItemUsageValue::Calculate()
{
uint32 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->CanUseItem(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)
{
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);
if (equip != ITEM_USAGE_NONE)
return equip;
if ((proto->Class == ITEM_CLASS_ARMOR || proto->Class == ITEM_CLASS_WEAPON) && proto->Bonding != BIND_WHEN_PICKED_UP &&
botAI->HasSkill(SKILL_ENCHANTING) && proto->Quality >= ITEM_QUALITY_UNCOMMON)
return ITEM_USAGE_DISENCHANT;
//While sync is on, do not loot quest items that are also Useful for master. Master
if (!botAI->GetMaster() || !sPlayerbotAIConfig->syncQuestWithPlayer || !IsItemUsefulForQuest(botAI->GetMaster(), proto))
if (IsItemUsefulForQuest(bot, proto))
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)
{
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
{
uint32 subClass = 0;
switch (pItem->GetTemplate()->SubClass)
{
case ITEM_SUBCLASS_WEAPON_GUN:
subClass = ITEM_SUBCLASS_BULLET;
break;
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
subClass = ITEM_SUBCLASS_ARROW;
break;
}
if (proto->SubClass == subClass)
{
float ammo = BetterStacks(proto, "ammo");
float needAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2;
if (ammo < needAmmo) //We already have enough of the current ammo.
{
ammo += CurrentStacks(proto);
if (ammo < needAmmo) //Buy ammo to get to the proper supply
return ITEM_USAGE_AMMO;
else if (ammo < needAmmo + 1)
return ITEM_USAGE_KEEP; // Keep the ammo until we have too much.
}
}
}
}
//Need to add something like free bagspace or item value.
if (proto->SellPrice > 0)
{
if (proto->Quality > ITEM_QUALITY_NORMAL)
{
return ITEM_USAGE_AH;
}
else
{
return ITEM_USAGE_VENDOR;
}
}
return ITEM_USAGE_NONE;
}
ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
{
if (bot->CanUseItem(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);
if (!pItem)
return ITEM_USAGE_NONE;
uint16 dest;
InventoryResult result = bot->CanEquipItem(NULL_SLOT, dest, pItem, true, false);
pItem->RemoveFromUpdateQueueOf(bot);
delete pItem;
if (result != EQUIP_ERR_OK )
return ITEM_USAGE_NONE;
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);
if (statWeight)
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;
Item* oldItem = bot->GetItemByPos(dest);
//No item equiped
if (!oldItem)
if (shouldEquip)
return ITEM_USAGE_EQUIP;
else
return ITEM_USAGE_BAD_EQUIP;
ItemTemplate const* oldItemProto = oldItem->GetTemplate();
if (oldItem)
{
uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId);
if (statWeight || oldStatWeight)
{
shouldEquip = statWeight >= oldStatWeight;
}
}
// 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 (newItemPower > oldItemPower)
isBetter = true;
else if (newItemPower == oldItemPower && itemProto->Quality > oldItemProto->Quality)
isBetter = true;
else if (newItemPower == oldItemPower && 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 && (shouldEquip || !existingShouldEquip) && isBetter)
{
switch (itemProto->Class)
{
case ITEM_CLASS_ARMOR:
if (oldItemProto->SubClass <= itemProto->SubClass)
{
if (itemIsBroken && !oldItemIsBroken)
return ITEM_USAGE_BROKEN_EQUIP;
else if (shouldEquip)
return ITEM_USAGE_REPLACE;
else
return ITEM_USAGE_BAD_EQUIP;
break;
}
default:
{
if (itemIsBroken && !oldItemIsBroken)
return ITEM_USAGE_BROKEN_EQUIP;
else if (shouldEquip)
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)
{
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 entry = player->GetQuestSlotQuestId(slot);
Quest const* quest = sObjectMgr->GetQuestTemplate(entry);
if (!quest)
continue;
for (uint8 i = 0; i < 4; i++)
{
if (quest->RequiredItemId[i] != proto->ItemId)
continue;
if (GET_PLAYERBOT_AI(player) && AI_VALUE2(uint32, "item count", proto->Name1) >= quest->RequiredItemCount[i])
continue;
return true;
}
}
return false;
}
bool ItemUsageValue::IsItemNeededForSkill(ItemTemplate const* proto)
{
switch (proto->ItemId)
{
case 2901: //Mining pick
return botAI->HasSkill(SKILL_MINING);
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 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 "";
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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);
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

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "LastMovementValue.h"
#include "Playerbots.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;
}
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;
}
void LastMovement::Set(Unit* follow)
{
Set(0, 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)
{
lastMoveToMapId = mapId;
lastMoveToX = x;
lastMoveToY = y;
lastMoveToZ = z;
lastMoveToOri = ori;
lastFollow = nullptr;
lastMoveShort = WorldPosition(mapId, x, y, z, ori);
}
void LastMovement::setShort(WorldPosition point)
{
lastMoveShort = point; lastFollow = nullptr;
}
void LastMovement::setPath(TravelPath path)
{
lastPath = path;
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_LASTMOVEMENTVALUE_H
#define _PLAYERBOT_LASTMOVEMENTVALUE_H
#include "ObjectGuid.h"
#include "TravelNode.h"
#include "Value.h"
class PlayerbotAI;
class Unit;
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;
return *this;
};
void clear();
void Set(Unit* follow);
void Set(uint32 mapId, float x, float y, float z, float ori);
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;
WorldPosition lastMoveShort;
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

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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);
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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
{
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;
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "LootValues.h"
#include "SharedValueContext.h"
#include "Playerbots.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();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_LOOTVALUES_H
#define _PLAYERBOT_LOOTVALUES_H
#include "LootMgr.h"
#include "NamedObjectContext.h"
#include "ItemUsageValue.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) { }
itemUsageMap Calculate() override;
};
class HasUpgradeValue : public BoolCalculatedValue, public Qualified
{
public:
HasUpgradeValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "has upgrade", 2) { }
bool Calculate() override;
};
#endif

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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");
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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) { }
bool Calculate() override;
};
class ShouldHomeBindValue : public BoolCalculatedValue
{
public:
ShouldHomeBindValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "should home bind", 2) { }
bool Calculate() override;
};
class ShouldRepairValue : public BoolCalculatedValue
{
public:
ShouldRepairValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI,"should repair",2) { }
bool Calculate() override;
};
class CanRepairValue : public BoolCalculatedValue
{
public:
CanRepairValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can repair",2) { }
bool Calculate() override;
};
class ShouldSellValue : public BoolCalculatedValue
{
public:
ShouldSellValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "should sell",2) { }
bool Calculate() override;
};
class CanSellValue : public BoolCalculatedValue
{
public:
CanSellValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can sell",2) { }
bool Calculate() override;
};
class CanFightEqualValue: public BoolCalculatedValue
{
public:
CanFightEqualValue(PlayerbotAI* botAI) : BoolCalculatedValue(botAI, "can fight equal",2) { }
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

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,11 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "MasterTargetValue.h"
#include "Playerbots.h"
Unit* MasterTargetValue::Calculate()
{
return botAI->GetGroupMaster();
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_MASTERTARGETVALUE_H
#define _PLAYERBOT_MASTERTARGETVALUE_H
#include "Value.h"
class PlayerbotAI;
class Unit;
class MasterTargetValue : public UnitCalculatedValue
{
public:
MasterTargetValue(PlayerbotAI* botAI, std::string const name = "master target") : UnitCalculatedValue(botAI, name) { }
Unit* Calculate() override;
};
#endif

View File

@@ -0,0 +1,12 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NearestAdsValue.h"
#include "Playerbots.h"
bool NearestAddsValue::AcceptUnit(Unit* unit)
{
Unit* target = AI_VALUE(Unit*, "current target");
return unit != target;
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NEARESTADSVALUE_H
#define _PLAYERBOT_NEARESTADSVALUE_H
#include "PossibleTargetsValue.h"
#include "PlayerbotAIConfig.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

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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() && i_obj->IsWithinDistInMap(u, i_range);
}
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::VisitAllObjects(bot, searcher, range);
}
bool NearestCorpsesValue::AcceptUnit(Unit* unit)
{
return true;
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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) { }
protected:
void FindUnits(std::list<Unit*>& targets) override;
bool AcceptUnit(Unit* unit) override;
};
#endif

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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::VisitAllObjects(bot, searcher, range);
}
bool NearestFriendlyPlayersValue::AcceptUnit(Unit* unit)
{
ObjectGuid guid = unit->GetGUID();
return guid.IsPlayer() && guid != botAI->GetBot()->GetGUID();
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#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

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NearestGameObjects.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Playerbots.h"
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;
};
GuidVector NearestGameObjects::Calculate()
{
std::list<GameObject*> targets;
AnyGameObjectInObjectRangeCheck u_check(bot, range);
Acore::GameObjectListSearcher<AnyGameObjectInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitAllObjects(bot, searcher, range);
GuidVector result;
for (GameObject* go : targets)
{
if (ignoreLos || bot->IsWithinLOSInMap(go))
result.push_back(go->GetGUID());
}
return result;
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NEARESTGAMEOBJECTS_H
#define _PLAYERBOT_NEARESTGAMEOBJECTS_H
#include "Value.h"
#include "PlayerbotAIConfig.h"
class PlayerbotAI;
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), range(range) , ignoreLos(ignoreLos) { }
protected:
GuidVector Calculate() override;
private:
float range;
bool ignoreLos;
};
#endif

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "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::VisitAllObjects(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());
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NEARESTNONBOTPLAYERSVALUE_H
#define _PLAYERBOT_NEARESTNONBOTPLAYERSVALUE_H
#include "NearestUnitsValue.h"
#include "PlayerbotAIConfig.h"
class PlayerbotAI;
class NearestNonBotPlayersValue : public NearestUnitsValue
{
public:
NearestNonBotPlayersValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->grindDistance) : NearestUnitsValue(botAI, "nearest non bot players", range, true) { }
protected:
void FindUnits(std::list<Unit*>& targets) override;
bool AcceptUnit(Unit* unit) override;
};
#endif

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NearestNpcsValue.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Playerbots.h"
#include "Vehicle.h"
void NearestNpcsValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestNpcsValue::AcceptUnit(Unit* unit)
{
return !unit->IsHostileTo(bot) && !unit->IsPlayer();
}
void NearestVehiclesValue::FindUnits(std::list<Unit*>& targets)
{
Acore::AnyUnitInObjectRangeCheck u_check(bot, range);
Acore::UnitListSearcher<Acore::AnyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitAllObjects(bot, searcher, range);
}
bool NearestVehiclesValue::AcceptUnit(Unit* unit)
{
if (!unit || !unit->IsVehicle() || !unit->IsAlive())
return false;
Vehicle* veh = unit->GetVehicle();
if (!veh || !veh->GetAvailableSeatCount())
return false;
return true;
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_NEARESTNPCSVALUE_H
#define _PLAYERBOT_NEARESTNPCSVALUE_H
#include "NearestUnitsValue.h"
#include "PlayerbotAIConfig.h"
class PlayerbotAI;
class NearestNpcsValue : public NearestUnitsValue
{
public:
NearestNpcsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : NearestUnitsValue(botAI, "nearest npcs", range) { }
protected:
void FindUnits(std::list<Unit*>& targets) override;
bool AcceptUnit(Unit* unit) override;
};
class NearestVehiclesValue : public NearestUnitsValue
{
public:
NearestVehiclesValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : NearestUnitsValue(botAI, "nearest vehicles", range) { }
protected:
void FindUnits(std::list<Unit*>& targets) override;
bool AcceptUnit(Unit* unit) override;
};
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "NearestUnitsValue.h"
#include "Playerbots.h"
GuidVector NearestUnitsValue::Calculate()
{
std::list<Unit*> targets;
FindUnits(targets);
GuidVector results;
for (Unit* unit : targets)
{
if ((ignoreLos || bot->IsWithinLOSInMap(unit)) && AcceptUnit(unit))
results.push_back(unit->GetGUID());
}
return results;
}

Some files were not shown because too many files have changed in this diff Show More