diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index d0c3b659..48e6fd00 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -3,6 +3,7 @@ */ #include "MovementActions.h" +#include "GameObject.h" #include "Map.h" #include "MotionMaster.h" #include "MoveSplineInitArgs.h" @@ -13,6 +14,7 @@ #include "PlayerbotAIConfig.h" #include "Random.h" #include "SharedDefines.h" +#include "SpellInfo.h" #include "TargetedMovementGenerator.h" #include "Event.h" #include "LastMovementValue.h" @@ -1486,17 +1488,21 @@ bool FleeWithPetAction::Execute(Event event) bool AvoidAoeAction::isUseful() { - return AI_VALUE(Aura*, "area debuff"); + 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 + // Case #1: Aura with dynamic object (e.g. rain of fire) if (AvoidAuraWithDynamicObj()) { return true; } - // Case #2: Trap game object with spell - // Case #3: Trigger npc + // 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; } @@ -1506,10 +1512,11 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() if (!aura) { return false; } - if (!aura->GetSpellInfo()) { + const SpellInfo* spellInfo = aura->GetSpellInfo(); + if (!spellInfo) { return false; } - if (!bot->HasAura(aura->GetSpellInfo()->Id)) { + if (!bot->HasAura(spellInfo->Id)) { return false; } DynamicObject* dynOwner = aura->GetDynobjOwner(); @@ -1520,6 +1527,69 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() 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 = 5.0f; + 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 possibleAngles; if (currentTarget) { @@ -1528,26 +1598,26 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() possibleAngles.push_back(angleLeft); possibleAngles.push_back(angleRight); } else { - float angleTo = bot->GetAngle(dynOwner) - M_PI; + 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 pos{bot->GetPositionX() + cos(angle) * fleeDis, + Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; // todo (Yunfan): check carefully - if (dynOwner->GetExactDist(pos) > farestDis) { - farestDis = dynOwner->GetExactDist(pos); - bestPos = pos; + 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 << "I'm avoiding aoe spell [" << aura->GetSpellInfo()->SpellName[0] << "]..."; + out << "I'm avoiding aoe spell " << name << "..."; bot->Say(out.str(), LANG_UNIVERSAL); return true; } diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 58507e0c..4596aeb3 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -76,6 +76,8 @@ class AvoidAoeAction : public MovementAction protected: bool AvoidAuraWithDynamicObj(); + bool AvoidGameObjectWithDamage(); + bool FleePostion(Position pos, float radius, std::string name); }; class RunAwayAction : public MovementAction diff --git a/src/strategy/values/NearestGameObjects.cpp b/src/strategy/values/NearestGameObjects.cpp index 3380d4a2..fe8f5c9e 100644 --- a/src/strategy/values/NearestGameObjects.cpp +++ b/src/strategy/values/NearestGameObjects.cpp @@ -8,6 +8,7 @@ #include "GridNotifiersImpl.h" #include "Playerbots.h" #include "SharedDefines.h" +#include "SpellMgr.h" class AnyGameObjectInObjectRangeCheck { @@ -58,15 +59,32 @@ GuidVector NearestTrapWithDamageValue::Calculate() { continue; } - uint32 spellId = go->GetSpellId(); + const GameObjectTemplate* goInfo = go->GetGOInfo(); + if (!goInfo) + { + continue; + } + uint32 spellId = goInfo->trap.spellId; if (!spellId) { continue; } - // if (ignoreLos || bot->IsWithinLOSInMap(go)) - result.push_back(go->GetGUID()); + 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; } diff --git a/src/strategy/values/NearestGameObjects.h b/src/strategy/values/NearestGameObjects.h index 2321c728..d0e7850f 100644 --- a/src/strategy/values/NearestGameObjects.h +++ b/src/strategy/values/NearestGameObjects.h @@ -27,7 +27,7 @@ class NearestGameObjects : public ObjectGuidListCalculatedValue class NearestTrapWithDamageValue : public ObjectGuidListCalculatedValue { public: - NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : + NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = 10.0f) : ObjectGuidListCalculatedValue(botAI, "nearest trap with damage", 1 * 1000), range(range) { } protected: