mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
@@ -313,7 +313,6 @@ AiPlayerbot.AlmostFullHealth = 85
|
|||||||
AiPlayerbot.LowMana = 15
|
AiPlayerbot.LowMana = 15
|
||||||
AiPlayerbot.MediumMana = 40
|
AiPlayerbot.MediumMana = 40
|
||||||
|
|
||||||
|
|
||||||
# Enable healer bot save mana
|
# Enable healer bot save mana
|
||||||
# Default: 1 (enable)
|
# Default: 1 (enable)
|
||||||
AiPlayerbot.AutoSaveMana = 1
|
AiPlayerbot.AutoSaveMana = 1
|
||||||
@@ -322,6 +321,9 @@ AiPlayerbot.AutoSaveMana = 1
|
|||||||
# Default: 60 (60%)
|
# Default: 60 (60%)
|
||||||
AiPlayerbot.SaveManaThreshold = 60
|
AiPlayerbot.SaveManaThreshold = 60
|
||||||
|
|
||||||
|
# Enable auto avoid aoe (experimental)
|
||||||
|
# Default: 0 (disable)
|
||||||
|
AiPlayerbot.AutoAvoidAoe = 0
|
||||||
|
|
||||||
# Random bot default strategies (applied after defaults)
|
# Random bot default strategies (applied after defaults)
|
||||||
AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat"
|
AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat"
|
||||||
|
|||||||
@@ -266,9 +266,14 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa
|
|||||||
{
|
{
|
||||||
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
|
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
|
||||||
}
|
}
|
||||||
if (sPlayerbotAIConfig->autoSaveMana) {
|
if (sPlayerbotAIConfig->autoSaveMana)
|
||||||
|
{
|
||||||
engine->addStrategy("auto save mana");
|
engine->addStrategy("auto save mana");
|
||||||
}
|
}
|
||||||
|
if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster())
|
||||||
|
{
|
||||||
|
engine->addStrategy("avoid aoe");
|
||||||
|
}
|
||||||
switch (player->getClass())
|
switch (player->getClass())
|
||||||
{
|
{
|
||||||
case CLASS_PRIEST:
|
case CLASS_PRIEST:
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
mediumMana = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumMana", 40);
|
mediumMana = sConfigMgr->GetOption<int32>("AiPlayerbot.MediumMana", 40);
|
||||||
autoSaveMana = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoSaveMana", true);
|
autoSaveMana = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoSaveMana", true);
|
||||||
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
|
saveManaThreshold = sConfigMgr->GetOption<int32>("AiPlayerbot.SaveManaThreshold", 60);
|
||||||
|
autoAvoidAoe = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoAvoidAoe", false);
|
||||||
|
|
||||||
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.15f);
|
randomGearLoweringChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomGearLoweringChance", 0.15f);
|
||||||
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.15f);
|
randomBotMaxLevelChance = sConfigMgr->GetOption<float>("AiPlayerbot.RandomBotMaxLevelChance", 0.15f);
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ class PlayerbotAIConfig
|
|||||||
uint32 lowMana, mediumMana;
|
uint32 lowMana, mediumMana;
|
||||||
bool autoSaveMana;
|
bool autoSaveMana;
|
||||||
uint32 saveManaThreshold;
|
uint32 saveManaThreshold;
|
||||||
|
bool autoAvoidAoe;
|
||||||
|
|
||||||
uint32 openGoSpell;
|
uint32 openGoSpell;
|
||||||
bool randomBotAutologin;
|
bool randomBotAutologin;
|
||||||
bool botAutologin;
|
bool botAutologin;
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ class ActionContext : public NamedObjectContext<Action>
|
|||||||
creators["reach party member to resurrect"] = &ActionContext::reach_party_member_to_resurrect;
|
creators["reach party member to resurrect"] = &ActionContext::reach_party_member_to_resurrect;
|
||||||
creators["flee"] = &ActionContext::flee;
|
creators["flee"] = &ActionContext::flee;
|
||||||
creators["flee with pet"] = &ActionContext::flee_with_pet;
|
creators["flee with pet"] = &ActionContext::flee_with_pet;
|
||||||
|
creators["avoid aoe"] = &ActionContext::avoid_aoe;
|
||||||
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
|
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
|
||||||
creators["shoot"] = &ActionContext::shoot;
|
creators["shoot"] = &ActionContext::shoot;
|
||||||
creators["lifeblood"] = &ActionContext::lifeblood;
|
creators["lifeblood"] = &ActionContext::lifeblood;
|
||||||
@@ -263,6 +264,7 @@ class ActionContext : public NamedObjectContext<Action>
|
|||||||
static Action* reach_party_member_to_resurrect(PlayerbotAI* botAI) { return new ReachPartyMemberToResurrectAction(botAI); }
|
static Action* reach_party_member_to_resurrect(PlayerbotAI* botAI) { return new ReachPartyMemberToResurrectAction(botAI); }
|
||||||
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
|
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
|
||||||
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
|
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
|
||||||
|
static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); }
|
||||||
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
|
||||||
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
|
||||||
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
|
#include "GameObject.h"
|
||||||
#include "Map.h"
|
#include "Map.h"
|
||||||
#include "MotionMaster.h"
|
#include "MotionMaster.h"
|
||||||
#include "MoveSplineInitArgs.h"
|
#include "MoveSplineInitArgs.h"
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
#include "Random.h"
|
#include "Random.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
|
#include "SpellInfo.h"
|
||||||
#include "TargetedMovementGenerator.h"
|
#include "TargetedMovementGenerator.h"
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "LastMovementValue.h"
|
#include "LastMovementValue.h"
|
||||||
@@ -155,22 +157,24 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
!bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater();
|
!bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater();
|
||||||
if (!generatePath) {
|
if (!generatePath) {
|
||||||
float distance = bot->GetExactDist(x, y, z);
|
float distance = bot->GetExactDist(x, y, z);
|
||||||
WaitForReach(distance);
|
if (distance > sPlayerbotAIConfig->contactDistance)
|
||||||
|
|
||||||
if (bot->IsSitState())
|
|
||||||
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
|
||||||
|
|
||||||
if (bot->IsNonMeleeSpellCast(true))
|
|
||||||
{
|
{
|
||||||
bot->CastStop();
|
WaitForReach(distance);
|
||||||
botAI->InterruptSpell();
|
|
||||||
|
if (bot->IsSitState())
|
||||||
|
bot->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
|
|
||||||
|
if (bot->IsNonMeleeSpellCast(true))
|
||||||
|
{
|
||||||
|
bot->CastStop();
|
||||||
|
botAI->InterruptSpell();
|
||||||
|
}
|
||||||
|
MotionMaster &mm = *bot->GetMotionMaster();
|
||||||
|
mm.Clear();
|
||||||
|
mm.MovePoint(mapId, x, y, z, generatePath);
|
||||||
|
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
MotionMaster &mm = *bot->GetMotionMaster();
|
|
||||||
|
|
||||||
mm.Clear();
|
|
||||||
mm.MovePoint(mapId, x, y, z, generatePath);
|
|
||||||
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation());
|
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
float modifiedZ;
|
float modifiedZ;
|
||||||
Movement::PointsArray path = SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig->maxMovementSearchTime);
|
Movement::PointsArray path = SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig->maxMovementSearchTime);
|
||||||
@@ -197,7 +201,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
|
|||||||
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation());
|
AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1482,6 +1485,145 @@ bool FleeWithPetAction::Execute(Event event)
|
|||||||
return Flee(AI_VALUE(Unit*, "current target"));
|
return Flee(AI_VALUE(Unit*, "current target"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AvoidAoeAction::isUseful()
|
||||||
|
{
|
||||||
|
GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage");
|
||||||
|
return AI_VALUE(Aura*, "area debuff") || !traps.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvoidAoeAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
// Case #1: Aura with dynamic object (e.g. rain of fire)
|
||||||
|
if (AvoidAuraWithDynamicObj()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Case #2: Trap game object with spell (e.g. lava bomb)
|
||||||
|
if (AvoidGameObjectWithDamage()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Case #3: Trigger npc (e.g. Lesser shadow fissure)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvoidAoeAction::AvoidAuraWithDynamicObj()
|
||||||
|
{
|
||||||
|
Aura* aura = AI_VALUE(Aura*, "area debuff");
|
||||||
|
if (!aura) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const SpellInfo* spellInfo = aura->GetSpellInfo();
|
||||||
|
if (!spellInfo) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bot->HasAura(spellInfo->Id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DynamicObject* dynOwner = aura->GetDynobjOwner();
|
||||||
|
if (!dynOwner || !dynOwner->IsInWorld()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
float radius = dynOwner->GetRadius();
|
||||||
|
if (bot->GetDistance(dynOwner) > radius) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::ostringstream name;
|
||||||
|
name << "[" << spellInfo->SpellName[0] << "] (aura)";
|
||||||
|
if (FleePostion(dynOwner->GetPosition(), radius, name.str())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvoidAoeAction::AvoidGameObjectWithDamage()
|
||||||
|
{
|
||||||
|
GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage");
|
||||||
|
if (traps.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (ObjectGuid &guid : traps) {
|
||||||
|
GameObject* go = botAI->GetGameObject(guid);
|
||||||
|
if (!go || !go->IsInWorld()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (go->GetGoType() != GAMEOBJECT_TYPE_TRAP)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const GameObjectTemplate* goInfo = go->GetGOInfo();
|
||||||
|
if (!goInfo)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint32 spellId = goInfo->trap.spellId;
|
||||||
|
if (!spellId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
|
if (spellInfo->IsPositive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float radius = (float)goInfo->trap.diameter / 2;
|
||||||
|
// for (int i = 0; i < MAX_SPELL_EFFECTS; i++) {
|
||||||
|
// if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) {
|
||||||
|
// if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) {
|
||||||
|
// radius = spellInfo->Effects[i].CalcRadius();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// } else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
if (bot->GetDistance(go) > radius) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::ostringstream name;
|
||||||
|
name << "[" << spellInfo->SpellName[0] << "] (object)";
|
||||||
|
if (FleePostion(go->GetPosition(), radius, name.str())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvoidAoeAction::FleePostion(Position pos, float radius, std::string name)
|
||||||
|
{
|
||||||
|
Unit* currentTarget = AI_VALUE(Unit*, "current target");
|
||||||
|
std::vector<float> possibleAngles;
|
||||||
|
if (currentTarget) {
|
||||||
|
float angleLeft = bot->GetAngle(currentTarget) + M_PI / 2;
|
||||||
|
float angleRight = bot->GetAngle(currentTarget) - M_PI / 2;
|
||||||
|
possibleAngles.push_back(angleLeft);
|
||||||
|
possibleAngles.push_back(angleRight);
|
||||||
|
} else {
|
||||||
|
float angleTo = bot->GetAngle(&pos) - M_PI;
|
||||||
|
possibleAngles.push_back(angleTo);
|
||||||
|
}
|
||||||
|
float farestDis = 0.0f;
|
||||||
|
Position bestPos;
|
||||||
|
for (float &angle : possibleAngles) {
|
||||||
|
float fleeDis = sPlayerbotAIConfig->fleeDistance;
|
||||||
|
Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis,
|
||||||
|
bot->GetPositionY() + sin(angle) * fleeDis,
|
||||||
|
bot->GetPositionZ()};
|
||||||
|
// todo (Yunfan): check carefully
|
||||||
|
if (pos.GetExactDist(fleePos) > farestDis) {
|
||||||
|
farestDis = pos.GetExactDist(fleePos);
|
||||||
|
bestPos = fleePos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (farestDis > 0.0f) {
|
||||||
|
if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) {
|
||||||
|
std::ostringstream out;
|
||||||
|
out << "Avoiding spell " << name << "...";
|
||||||
|
bot->Say(out.str(), LANG_UNIVERSAL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool RunAwayAction::Execute(Event event)
|
bool RunAwayAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
return Flee(AI_VALUE(Unit*, "master target"));
|
return Flee(AI_VALUE(Unit*, "master target"));
|
||||||
|
|||||||
@@ -66,11 +66,25 @@ class FleeWithPetAction : public MovementAction
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AvoidAoeAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AvoidAoeAction(PlayerbotAI* botAI) : MovementAction(botAI, "avoid aoe") { }
|
||||||
|
|
||||||
|
bool isUseful() override;
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool AvoidAuraWithDynamicObj();
|
||||||
|
bool AvoidGameObjectWithDamage();
|
||||||
|
bool FleePostion(Position pos, float radius, std::string name);
|
||||||
|
};
|
||||||
|
|
||||||
class RunAwayAction : public MovementAction
|
class RunAwayAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RunAwayAction(PlayerbotAI* botAI) : MovementAction(botAI, "runaway") { }
|
RunAwayAction(PlayerbotAI* botAI) : MovementAction(botAI, "runaway") { }
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ bool TellLosAction::Execute(Event event)
|
|||||||
ListUnits("--- Friendly players ---", *context->GetValue<GuidVector>("nearest friendly players"));
|
ListUnits("--- Friendly players ---", *context->GetValue<GuidVector>("nearest friendly players"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (param.empty() || param == "triggers")
|
||||||
|
{
|
||||||
|
ListUnits("--- Triggers ---", *context->GetValue<GuidVector>("possible triggers"));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +51,9 @@ void TellLosAction::ListUnits(std::string const title, GuidVector units)
|
|||||||
|
|
||||||
for (ObjectGuid const guid : units)
|
for (ObjectGuid const guid : units)
|
||||||
{
|
{
|
||||||
if (Unit* unit = botAI->GetUnit(guid))
|
if (Unit* unit = botAI->GetUnit(guid)) {
|
||||||
botAI->TellMaster(unit->GetName());
|
botAI->TellMaster(unit->GetName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "CombatStrategy.h"
|
#include "CombatStrategy.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "Strategy.h"
|
||||||
|
|
||||||
void CombatStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
void CombatStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
|
||||||
{
|
{
|
||||||
@@ -62,15 +63,22 @@ float AvoidAoeStrategyMultiplier::GetValue(Action* action)
|
|||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NextAction** AvoidAoeStrategy::getDefaultActions()
|
||||||
|
{
|
||||||
|
return NextAction::array(0,
|
||||||
|
new NextAction("avoid aoe", ACTION_EMERGENCY),
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void AvoidAoeStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode(
|
// triggers.push_back(new TriggerNode(
|
||||||
"has area debuff",
|
// "has area debuff",
|
||||||
NextAction::array(0, new NextAction("flee", ACTION_EMERGENCY + 5), NULL)));
|
// NextAction::array(0, new NextAction("flee", ACTION_EMERGENCY + 5), NULL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void AvoidAoeStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
{
|
{
|
||||||
multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI));
|
// multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI));
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ class AvoidAoeStrategy : public Strategy
|
|||||||
public:
|
public:
|
||||||
explicit AvoidAoeStrategy(PlayerbotAI* ai);
|
explicit AvoidAoeStrategy(PlayerbotAI* ai);
|
||||||
const std::string getName() override { return "avoid aoe"; }
|
const std::string getName() override { return "avoid aoe"; }
|
||||||
|
NextAction** getDefaultActions() override;
|
||||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ class TriggerContext : public NamedObjectContext<Trigger>
|
|||||||
|
|
||||||
creators["has area debuff"] = &TriggerContext::HasAreaDebuff;
|
creators["has area debuff"] = &TriggerContext::HasAreaDebuff;
|
||||||
|
|
||||||
|
|
||||||
creators["enemy out of melee"] = &TriggerContext::EnemyOutOfMelee;
|
creators["enemy out of melee"] = &TriggerContext::EnemyOutOfMelee;
|
||||||
creators["enemy out of spell"] = &TriggerContext::EnemyOutOfSpell;
|
creators["enemy out of spell"] = &TriggerContext::EnemyOutOfSpell;
|
||||||
creators["enemy too close for spell"] = &TriggerContext::enemy_too_close_for_spell;
|
creators["enemy too close for spell"] = &TriggerContext::enemy_too_close_for_spell;
|
||||||
|
|||||||
@@ -116,4 +116,27 @@ bool HasAreaDebuffValue::Calculate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aura* AreaDebuffValue::Calculate()
|
||||||
|
{
|
||||||
|
Unit::AuraApplicationMap& map = bot->GetAppliedAuras();
|
||||||
|
for (Unit::AuraApplicationMap::iterator i = map.begin(); i != map.end(); ++i)
|
||||||
|
{
|
||||||
|
Aura *aura = i->second->GetBase();
|
||||||
|
if (!aura)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
AuraObjectType type = aura->GetType();
|
||||||
|
// bool is_area = aura->IsArea();
|
||||||
|
bool isPositive = aura->GetSpellInfo()->IsPositive();
|
||||||
|
if (type == DYNOBJ_AURA_TYPE && !isPositive) {
|
||||||
|
DynamicObject* dynOwner = aura->GetDynobjOwner();
|
||||||
|
if (!dynOwner) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return aura;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#ifndef _PLAYERBOT_AOEVALUES_H
|
#ifndef _PLAYERBOT_AOEVALUES_H
|
||||||
#define _PLAYERBOT_AOEVALUES_H
|
#define _PLAYERBOT_AOEVALUES_H
|
||||||
|
|
||||||
|
#include "GameObject.h"
|
||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
@@ -41,4 +42,13 @@ class HasAreaDebuffValue : public BoolCalculatedValue, public Qualified
|
|||||||
virtual bool Calculate();
|
virtual bool Calculate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AreaDebuffValue : public CalculatedValue<Aura*>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AreaDebuffValue(PlayerbotAI* botAI) :
|
||||||
|
CalculatedValue<Aura*>(botAI, "area debuff", 1 * 1000) { }
|
||||||
|
|
||||||
|
Aura* Calculate() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -33,4 +33,5 @@ class ExpectedGroupDpsValue : public FloatCalculatedValue
|
|||||||
public:
|
public:
|
||||||
float Calculate() override;
|
float Calculate() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include "GridNotifiers.h"
|
#include "GridNotifiers.h"
|
||||||
#include "GridNotifiersImpl.h"
|
#include "GridNotifiersImpl.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "SharedDefines.h"
|
||||||
|
#include "SpellMgr.h"
|
||||||
|
|
||||||
class AnyGameObjectInObjectRangeCheck
|
class AnyGameObjectInObjectRangeCheck
|
||||||
{
|
{
|
||||||
@@ -42,3 +44,52 @@ GuidVector NearestGameObjects::Calculate()
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GuidVector NearestTrapWithDamageValue::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 (go->GetGoType() != GAMEOBJECT_TYPE_TRAP)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Unit* owner = go->GetOwner();
|
||||||
|
if (owner && owner->IsFriendlyTo(bot))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const GameObjectTemplate* goInfo = go->GetGOInfo();
|
||||||
|
if (!goInfo)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint32 spellId = goInfo->trap.spellId;
|
||||||
|
if (!spellId)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
|
if (spellInfo->IsPositive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < MAX_SPELL_EFFECTS; i++) {
|
||||||
|
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) {
|
||||||
|
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) {
|
||||||
|
result.push_back(go->GetGUID());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) {
|
||||||
|
result.push_back(go->GetGUID());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,4 +24,17 @@ class NearestGameObjects : public ObjectGuidListCalculatedValue
|
|||||||
bool ignoreLos;
|
bool ignoreLos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NearestTrapWithDamageValue : public ObjectGuidListCalculatedValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = 10.0f) :
|
||||||
|
ObjectGuidListCalculatedValue(botAI, "nearest trap with damage", 1 * 1000), range(range) { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GuidVector Calculate() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float range;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "GridNotifiers.h"
|
#include "GridNotifiers.h"
|
||||||
#include "GridNotifiersImpl.h"
|
#include "GridNotifiersImpl.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
|
void PossibleTargetsValue::FindUnits(std::list<Unit*>& targets)
|
||||||
{
|
{
|
||||||
@@ -20,3 +21,17 @@ bool PossibleTargetsValue::AcceptUnit(Unit* unit)
|
|||||||
{
|
{
|
||||||
return AttackersValue::IsPossibleTarget(unit, bot, range);
|
return AttackersValue::IsPossibleTarget(unit, bot, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PossibleTriggersValue::FindUnits(std::list<Unit*>& targets)
|
||||||
|
{
|
||||||
|
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, range);
|
||||||
|
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
|
||||||
|
Cell::VisitAllObjects(bot, searcher, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PossibleTriggersValue::AcceptUnit(Unit* unit)
|
||||||
|
{
|
||||||
|
return unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) && unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
|
||||||
|
return true; // AttackersValue::IsPossibleTarget(unit, bot, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,4 +27,14 @@ class AllTargetsValue : public PossibleTargetsValue
|
|||||||
AllTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : PossibleTargetsValue(botAI, "all targets", range, true) { }
|
AllTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : PossibleTargetsValue(botAI, "all targets", range, true) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PossibleTriggersValue : public NearestUnitsValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PossibleTriggersValue(PlayerbotAI* botAI, std::string const name = "possible targets", float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = true):
|
||||||
|
NearestUnitsValue(botAI, name, range, ignoreLos) { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void FindUnits(std::list<Unit*>& targets) override;
|
||||||
|
bool AcceptUnit(Unit* unit) override;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
|||||||
creators["nearest enemy players"] = &ValueContext::nearest_enemy_players;
|
creators["nearest enemy players"] = &ValueContext::nearest_enemy_players;
|
||||||
creators["possible targets"] = &ValueContext::possible_targets;
|
creators["possible targets"] = &ValueContext::possible_targets;
|
||||||
creators["possible targets no los"] = &ValueContext::possible_targets_no_los;
|
creators["possible targets no los"] = &ValueContext::possible_targets_no_los;
|
||||||
|
creators["possible triggers"] = &ValueContext::possible_triggers;
|
||||||
creators["possible adds"] = &ValueContext::possible_adds;
|
creators["possible adds"] = &ValueContext::possible_adds;
|
||||||
creators["all targets"] = &ValueContext::all_targets;
|
creators["all targets"] = &ValueContext::all_targets;
|
||||||
creators["possible rpg targets"] = &ValueContext::possible_rpg_targets;
|
creators["possible rpg targets"] = &ValueContext::possible_rpg_targets;
|
||||||
@@ -295,8 +296,10 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
|||||||
creators["boss target"] = &ValueContext::boss_target;
|
creators["boss target"] = &ValueContext::boss_target;
|
||||||
creators["nearest triggers"] = &ValueContext::nearest_triggers;
|
creators["nearest triggers"] = &ValueContext::nearest_triggers;
|
||||||
creators["neglect threat"] = &ValueContext::neglect_threat;
|
creators["neglect threat"] = &ValueContext::neglect_threat;
|
||||||
creators["expected lifetime"] = &ValueContext::expected_lifetime;
|
creators["expected lifetime"] = &ValueContext::expected_lifetime;
|
||||||
creators["expected group dps"] = &ValueContext::expected_group_dps;
|
creators["expected group dps"] = &ValueContext::expected_group_dps;
|
||||||
|
creators["area debuff"] = &ValueContext::area_debuff;
|
||||||
|
creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -376,6 +379,7 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
|||||||
static UntypedValue* nearest_corpses(PlayerbotAI* botAI) { return new NearestCorpsesValue(botAI); }
|
static UntypedValue* nearest_corpses(PlayerbotAI* botAI) { return new NearestCorpsesValue(botAI); }
|
||||||
static UntypedValue* possible_rpg_targets(PlayerbotAI* botAI) { return new PossibleRpgTargetsValue(botAI); }
|
static UntypedValue* possible_rpg_targets(PlayerbotAI* botAI) { return new PossibleRpgTargetsValue(botAI); }
|
||||||
static UntypedValue* possible_targets(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI); }
|
static UntypedValue* possible_targets(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI); }
|
||||||
|
static UntypedValue* possible_triggers(PlayerbotAI* botAI) { return new PossibleTriggersValue(botAI); }
|
||||||
static UntypedValue* possible_targets_no_los(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI, "possible targets", sPlayerbotAIConfig->sightDistance, true); }
|
static UntypedValue* possible_targets_no_los(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI, "possible targets", sPlayerbotAIConfig->sightDistance, true); }
|
||||||
static UntypedValue* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsValue(botAI); }
|
static UntypedValue* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsValue(botAI); }
|
||||||
static UntypedValue* all_targets(PlayerbotAI* botAI) { return new AllTargetsValue(botAI); }
|
static UntypedValue* all_targets(PlayerbotAI* botAI) { return new AllTargetsValue(botAI); }
|
||||||
@@ -497,7 +501,8 @@ class ValueContext : public NamedObjectContext<UntypedValue>
|
|||||||
static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); }
|
static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); }
|
||||||
static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); }
|
static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); }
|
||||||
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); }
|
static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); }
|
||||||
|
static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); }
|
||||||
|
static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -42,3 +42,8 @@ bool UnstableAfflictionOnAttackerTrigger::IsActive()
|
|||||||
// !botAI->HasAura("unstable affliction", GetTarget(), false, true);
|
// !botAI->HasAura("unstable affliction", GetTarget(), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DecimationTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Aura *aura = botAI->GetAura(getName(), GetTarget(), false, true);
|
||||||
|
return aura && aura->GetDuration() > 3000;
|
||||||
|
}
|
||||||
@@ -142,6 +142,7 @@ class DecimationTrigger : public HasAuraTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DecimationTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "decimation") {}
|
DecimationTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "decimation") {}
|
||||||
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MoltenCoreTrigger : public HasAuraTrigger
|
class MoltenCoreTrigger : public HasAuraTrigger
|
||||||
|
|||||||
Reference in New Issue
Block a user