#include "Playerbots.h" #include "ANActions.h" bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); } bool AttackWebWrapAction::Execute(Event /*event*/) { Unit* webWrap = nullptr; // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); for (auto i = targets.begin(); i != targets.end(); ++i) { Unit* unit = botAI->GetUnit(*i); if (unit && unit->GetEntry() == NPC_WEB_WRAP) { webWrap = unit; break; } } if (!webWrap || AI_VALUE(Unit*, "current target") == webWrap) { return false; } return Attack(webWrap); } bool WatchersTargetAction::isUseful() { return !botAI->IsHeal(bot); } bool WatchersTargetAction::Execute(Event /*event*/) { // Always prioritise web wraps Unit* currTarget = AI_VALUE(Unit*, "current target"); if (currTarget && currTarget->GetEntry() == NPC_WEB_WRAP) { return false; } // Do not search all units in range! // There are many adds we don't want to aggro in close proximity, // only check in-combat adds now. GuidVector attackers = AI_VALUE(GuidVector, "attackers"); Unit* priorityTargets[4] = {nullptr, nullptr, nullptr, nullptr}; for (auto& attacker : attackers) { Unit* npc = botAI->GetUnit(attacker); if (!npc) { continue; } switch (npc->GetEntry()) { // Focus skirmishers first case NPC_WATCHER_SKIRMISHER: priorityTargets[0] = npc; break; // Then shadowcaster. This doesn't work so well for the shadowcaster // + skirmisher pack - ideally we would kill the watcher second. // But don't want to make this unnecessarily complex and rigid... // Will revisit if this causes problems in heroic. case NPC_WATCHER_SHADOWCASTER: priorityTargets[1] = npc; break; // Named watcher next case NPC_WATCHER_SILTHIK: case NPC_WATCHER_GASHRA: case NPC_WATCHER_NARJIL: priorityTargets[2] = npc; break; // Warrior last case NPC_WATCHER_WARRIOR: priorityTargets[3] = npc; break; } } for (Unit* target : priorityTargets) { // Attack the first valid split target in the priority list if (target) { if (currTarget != target) { // bot->Yell("ATTACKING "+target->GetName(), LANG_UNIVERSAL); return Attack(target); } // Don't continue loop here, the target exists so we don't // want to move down the prio list. We just don't need to send attack // command again, just return false and exit the loop that way return false; } } return false; } bool AnubarakDodgePoundAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } bool AnubarakDodgePoundAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); if (!boss) { return false; } float distance = bot->GetExactDist2d(boss->GetPosition()); // Extra units to move into the boss, instead of being just 1 pixel past his midpoint. // Can be adjusted - this value tends to mirror how a human would play, // and visibly ensures you won't get hit while not creating excessive movements. float distanceExtra = 2.0f; return Move(bot->GetAngle(boss), distance + distanceExtra); }