mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-13 09:07:19 +00:00
1258 lines
44 KiB
C++
1258 lines
44 KiB
C++
#include "RaidKarazhanActions.h"
|
|
#include "RaidKarazhanHelpers.h"
|
|
#include "AiObjectContext.h"
|
|
#include "PlayerbotAI.h"
|
|
#include "PlayerbotMgr.h"
|
|
#include "PlayerbotTextMgr.h"
|
|
#include "Playerbots.h"
|
|
#include "Position.h"
|
|
|
|
namespace
|
|
{
|
|
// Big Bad Wolf
|
|
static int currentIndex = 0;
|
|
// Netherspite
|
|
static std::map<ObjectGuid, uint32> beamMoveTimes;
|
|
static std::map<ObjectGuid, bool> lastBeamMoveSideways;
|
|
}
|
|
|
|
bool KarazhanAttumenTheHuntsmanStackBehindAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Unit* boss = karazhanHelper.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
|
|
|
float distance = 5.0f;
|
|
float orientation = boss->GetOrientation() + M_PI;
|
|
float x = boss->GetPositionX();
|
|
float y = boss->GetPositionY();
|
|
float z = boss->GetPositionZ();
|
|
float rx = x + cos(orientation) * distance;
|
|
float ry = y + sin(orientation) * distance;
|
|
|
|
return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
|
}
|
|
|
|
bool KarazhanAttumenTheHuntsmanStackBehindAction::isUseful()
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Unit* boss = karazhanHelper.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
|
|
|
return boss && !(botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot);
|
|
}
|
|
|
|
bool KarazhanMoroesMarkTargetAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
|
|
Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe");
|
|
Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi");
|
|
Unit* keira = AI_VALUE2(Unit*, "find target", "lady keira berrybuck");
|
|
Unit* rafe = AI_VALUE2(Unit*, "find target", "baron rafe dreuger");
|
|
Unit* robin = AI_VALUE2(Unit*, "find target", "lord robin daris");
|
|
Unit* crispin = AI_VALUE2(Unit*, "find target", "lord crispin ference");
|
|
Unit* target = karazhanHelper.GetFirstAliveUnit({dorothea, catriona, keira, rafe, robin, crispin});
|
|
|
|
karazhanHelper.MarkTargetWithSkull(target);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanMaidenOfVirtuePositionBossAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
|
Unit* healer = nullptr;
|
|
|
|
if (Group* group = bot->GetGroup())
|
|
{
|
|
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
{
|
|
Player* member = itr->GetSource();
|
|
if (!member || !member->IsAlive() || !botAI->IsHeal(member) || !member->HasAura(SPELL_REPENTANCE))
|
|
{
|
|
continue;
|
|
}
|
|
healer = member;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (healer)
|
|
{
|
|
float angle = healer->GetOrientation();
|
|
float targetX = healer->GetPositionX() + cos(angle) * 6.0f;
|
|
float targetY = healer->GetPositionY() + sin(angle) * 6.0f;
|
|
float targetZ = healer->GetPositionZ();
|
|
{
|
|
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true,
|
|
MovementPriority::MOVEMENT_COMBAT);
|
|
}
|
|
}
|
|
|
|
const float maxDistance = 3.0f;
|
|
const float distanceToBossPosition = boss->GetExactDist2d(KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION);
|
|
|
|
if (distanceToBossPosition > maxDistance)
|
|
{
|
|
float dX = KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION.GetPositionX() - boss->GetPositionX();
|
|
float dY = KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION.GetPositionY() - boss->GetPositionY();
|
|
float mX = KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION.GetPositionX() + (dX / distanceToBossPosition) * maxDistance;
|
|
float mY = KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION.GetPositionY() + (dY / distanceToBossPosition) * maxDistance;
|
|
{
|
|
return MoveTo(bot->GetMapId(), mX, mY,
|
|
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanMaidenOfVirtuePositionBossAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
|
|
|
return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot;
|
|
}
|
|
|
|
bool KarazhanMaidenOfVirtuePositionRangedAction::Execute(Event event)
|
|
{
|
|
int maxIndex = 7;
|
|
int index = 0;
|
|
|
|
const GuidVector members = AI_VALUE(GuidVector, "group members");
|
|
|
|
for (const auto& memberGuid : members)
|
|
{
|
|
Unit* member = botAI->GetUnit(memberGuid);
|
|
|
|
if (!member || !botAI->IsRanged(member->ToPlayer()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (member == bot)
|
|
break;
|
|
|
|
if (index >= maxIndex)
|
|
{
|
|
index = 0;
|
|
continue;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
float distance = bot->GetExactDist2d(KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[index]);
|
|
const float maxDistance = 2.0f;
|
|
if (distance > maxDistance)
|
|
{
|
|
return MoveTo(bot->GetMapId(), KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[index].GetPositionX(),
|
|
KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[index].GetPositionY(), bot->GetPositionZ(), false,
|
|
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanMaidenOfVirtuePositionRangedAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
|
|
|
return boss && botAI->IsRanged(bot);
|
|
}
|
|
|
|
bool KarazhanBigBadWolfPositionBossAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
|
|
|
const float maxDistance = 3.0f;
|
|
const float distanceToBossPosition = boss->GetExactDist2d(KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION);
|
|
|
|
if (distanceToBossPosition > maxDistance)
|
|
{
|
|
float dX = KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION.GetPositionX() - boss->GetPositionX();
|
|
float dY = KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION.GetPositionY() - boss->GetPositionY();
|
|
|
|
float mX = KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION.GetPositionX() + (dX / distanceToBossPosition) * maxDistance;
|
|
float mY = KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION.GetPositionY() + (dY / distanceToBossPosition) * maxDistance;
|
|
|
|
float moveDistance = bot->GetExactDist2d(mX, mY);
|
|
if (moveDistance < 0.5f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return MoveTo(bot->GetMapId(), mX, mY, bot->GetPositionZ(), false, false, false, false,
|
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanBigBadWolfPositionBossAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
|
|
|
return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot &&
|
|
!bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD);
|
|
}
|
|
|
|
bool KarazhanBigBadWolfRunAwayAction::Execute(Event event)
|
|
{
|
|
constexpr float threshold = 1.0f;
|
|
Position target = KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[currentIndex];
|
|
|
|
while (bot->GetExactDist2d(target.GetPositionX(), target.GetPositionY()) < threshold)
|
|
{
|
|
currentIndex = (currentIndex + 1) % 4;
|
|
target = KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[currentIndex];
|
|
}
|
|
|
|
return MoveTo(bot->GetMapId(), target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(),
|
|
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
|
|
}
|
|
|
|
bool KarazhanBigBadWolfRunAwayAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
|
|
|
return boss && bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD);
|
|
}
|
|
|
|
bool KarazhanRomuloAndJulianneMarkTargetAction::Execute(Event event)
|
|
{
|
|
Unit* target = nullptr;
|
|
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
|
Unit* julianne = AI_VALUE2(Unit*, "find target", "julianne");
|
|
|
|
const int maxPctDifference = 10;
|
|
if (julianne->GetHealthPct() + maxPctDifference < romulo->GetHealthPct() || julianne->GetHealthPct() < 1.0f)
|
|
{
|
|
target = romulo;
|
|
}
|
|
else if (romulo->GetHealthPct() + maxPctDifference < julianne->GetHealthPct() || romulo->GetHealthPct() < 1.0f)
|
|
{
|
|
target = julianne;
|
|
}
|
|
if (!target)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
karazhanHelper.MarkTargetWithSkull(target);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanWizardOfOzMarkTargetAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
|
Unit* tito = AI_VALUE2(Unit*, "find target", "tito");
|
|
Unit* roar = AI_VALUE2(Unit*, "find target", "roar");
|
|
Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman");
|
|
Unit* tinhead = AI_VALUE2(Unit*, "find target", "tinhead");
|
|
Unit* crone = AI_VALUE2(Unit*, "find target", "the crone");
|
|
Unit* target = karazhanHelper.GetFirstAliveUnit({dorothee, tito, roar, strawman, tinhead, crone});
|
|
|
|
karazhanHelper.MarkTargetWithSkull(target);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanWizardOfOzScorchStrawmanAction::Execute(Event event)
|
|
{
|
|
Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman");
|
|
if (!strawman || !strawman->IsAlive() || bot->getClass() != CLASS_MAGE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (botAI->CanCastSpell("scorch", strawman))
|
|
{
|
|
botAI->CastSpell("scorch", strawman);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanTheCuratorMarkTargetAction::Execute(Event event)
|
|
{
|
|
Unit* target = AI_VALUE2(Unit*, "find target", "astral flare");
|
|
if (!target || !target->IsAlive())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
karazhanHelper.MarkTargetWithSkull(target);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanTheCuratorPositionBossAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "the curator");
|
|
const float maxDistance = 3.0f;
|
|
const float distanceToBossPosition = boss->GetExactDist2d(KARAZHAN_THE_CURATOR_BOSS_POSITION);
|
|
|
|
if (distanceToBossPosition > maxDistance)
|
|
{
|
|
float dX = KARAZHAN_THE_CURATOR_BOSS_POSITION.GetPositionX() - boss->GetPositionX();
|
|
float dY = KARAZHAN_THE_CURATOR_BOSS_POSITION.GetPositionY() - boss->GetPositionY();
|
|
float mX = KARAZHAN_THE_CURATOR_BOSS_POSITION.GetPositionX() + (dX / distanceToBossPosition) * maxDistance;
|
|
float mY = KARAZHAN_THE_CURATOR_BOSS_POSITION.GetPositionY() + (dY / distanceToBossPosition) * maxDistance;
|
|
{
|
|
return MoveTo(bot->GetMapId(), mX, mY,
|
|
bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true,
|
|
false);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanTheCuratorPositionBossAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "the curator");
|
|
|
|
return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot;
|
|
}
|
|
|
|
bool KarazhanTheCuratorSpreadRangedAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
const float minDistance = 5.0f;
|
|
Unit* nearestPlayer = karazhanHelper.GetNearestPlayerInRadius(minDistance);
|
|
|
|
if (nearestPlayer)
|
|
{
|
|
return FleePosition(nearestPlayer->GetPosition(), minDistance);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanTheCuratorSpreadRangedAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "the curator");
|
|
|
|
return boss && botAI->IsRanged(bot);
|
|
}
|
|
|
|
bool KarazhanTerestianIllhoofMarkTargetAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
|
if (!boss)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Unit* target = karazhanHelper.GetFirstAliveUnitByEntry(NPC_DEMON_CHAINS);
|
|
if (!target || !target->IsAlive())
|
|
{
|
|
target = karazhanHelper.GetFirstAliveUnitByEntry(NPC_KILREK);
|
|
if (!target || !target->IsAlive())
|
|
{
|
|
target = boss;
|
|
}
|
|
}
|
|
karazhanHelper.MarkTargetWithSkull(target);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanShadeOfAranArcaneExplosionRunAwayAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
const float safeDistance = 20.0f;
|
|
const float distance = bot->GetDistance2d(boss);
|
|
|
|
bot->AttackStop();
|
|
bot->InterruptNonMeleeSpells(false);
|
|
|
|
if (distance < safeDistance)
|
|
{
|
|
return MoveAway(boss, safeDistance - distance);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanShadeOfAranArcaneExplosionRunAwayAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
|
|
return boss && boss->IsAlive() && boss->HasUnitState(UNIT_STATE_CASTING) &&
|
|
boss->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION);
|
|
}
|
|
|
|
bool KarazhanShadeOfAranFlameWreathStopMovementAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
if (karazhanHelper.IsFlameWreathActive())
|
|
{
|
|
AI_VALUE(LastMovement&, "last movement").Set(nullptr);
|
|
bot->GetMotionMaster()->Clear();
|
|
if (bot->isMoving())
|
|
{
|
|
bot->StopMoving();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanShadeOfAranMarkConjuredElementalAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
Unit* target = karazhanHelper.GetFirstAliveUnitByEntry(NPC_CONJURED_ELEMENTAL);
|
|
|
|
if (!boss || !boss->IsAlive() ||
|
|
!target || !target->IsAlive() || target->HasAura(SPELL_WARLOCK_BANISH))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
karazhanHelper.MarkTargetWithSkull(target);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanShadeOfAranSpreadRangedAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
|
|
const float maxBossDistance = 12.0f;
|
|
float bossDistance = bot->GetExactDist2d(boss);
|
|
if (bossDistance > maxBossDistance)
|
|
{
|
|
float dX = bot->GetPositionX() - boss->GetPositionX();
|
|
float dY = bot->GetPositionY() - boss->GetPositionY();
|
|
float length = std::sqrt(dX * dX + dY * dY);
|
|
dX /= length;
|
|
dY /= length;
|
|
float tX = boss->GetPositionX() + dX * maxBossDistance;
|
|
float tY = boss->GetPositionY() + dY * maxBossDistance;
|
|
{
|
|
return MoveTo(bot->GetMapId(), tX, tY, bot->GetPositionZ(), false, false, false, true,
|
|
MovementPriority::MOVEMENT_COMBAT);
|
|
}
|
|
}
|
|
|
|
const float minDistance = 5.0f;
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Unit* nearestPlayer = karazhanHelper.GetNearestPlayerInRadius(minDistance);
|
|
if (nearestPlayer)
|
|
{
|
|
return FleePosition(nearestPlayer->GetPosition(), minDistance);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanShadeOfAranSpreadRangedAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
|
|
return boss && boss->IsAlive() && botAI->IsRanged(bot) && !karazhanHelper.IsFlameWreathActive() &&
|
|
!(boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION));
|
|
}
|
|
|
|
// One tank bot per phase will dance in and out of the red beam (5 seconds in, 5 seconds out)
|
|
// Tank bots will ignore void zones--their positioning is too important
|
|
bool KarazhanNetherspiteBlockRedBeamAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
|
|
|
|
Group* group = bot->GetGroup();
|
|
if (!group)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Player* eligibleTank = nullptr;
|
|
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
{
|
|
Player* member = itr->GetSource();
|
|
if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) ||
|
|
member->HasAura(SPELL_NETHER_EXHAUSTION_RED))
|
|
{
|
|
continue;
|
|
}
|
|
eligibleTank = member;
|
|
break;
|
|
}
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
Position beamPos = karazhanHelper.GetPositionOnBeam(boss, redPortal, 18.0f);
|
|
|
|
if (bot == eligibleTank)
|
|
{
|
|
std::map<std::string, std::string> ph;
|
|
ph["%player"] = bot->GetName();
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"netherspite_beam_blocking_red", "%player is moving to block the red beam!", ph);
|
|
bot->Yell(text, LANG_UNIVERSAL);
|
|
|
|
ObjectGuid botGuid = bot->GetGUID();
|
|
uint32 intervalSecs = 5;
|
|
|
|
if (beamMoveTimes[botGuid] == 0)
|
|
{
|
|
beamMoveTimes[botGuid] = time(nullptr);
|
|
lastBeamMoveSideways[botGuid] = false;
|
|
}
|
|
if (time(nullptr) - beamMoveTimes[botGuid] >= intervalSecs)
|
|
{
|
|
lastBeamMoveSideways[botGuid] = !lastBeamMoveSideways[botGuid];
|
|
beamMoveTimes[botGuid] = time(nullptr);
|
|
}
|
|
if (!lastBeamMoveSideways[botGuid])
|
|
{
|
|
return MoveTo(bot->GetMapId(), beamPos.GetPositionX(), beamPos.GetPositionY(), beamPos.GetPositionZ(),
|
|
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
|
|
}
|
|
|
|
else
|
|
{
|
|
float bx = boss->GetPositionX();
|
|
float by = boss->GetPositionY();
|
|
float px = redPortal->GetPositionX();
|
|
float py = redPortal->GetPositionY();
|
|
float dx = px - bx;
|
|
float dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
if (length == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
dx /= length;
|
|
dy /= length;
|
|
float perpDx = -dy;
|
|
float perpDy = dx;
|
|
float sideX = beamPos.GetPositionX() + perpDx * 3.0f;
|
|
float sideY = beamPos.GetPositionY() + perpDy * 3.0f;
|
|
float sideZ = beamPos.GetPositionZ();
|
|
|
|
return MoveTo(bot->GetMapId(), sideX, sideY, sideZ, false, false, false, true,
|
|
MovementPriority::MOVEMENT_FORCED);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanNetherspiteBlockRedBeamAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
|
|
|
|
ObjectGuid botGuid = bot->GetGUID();
|
|
static std::map<ObjectGuid, bool> lastBossBanishState;
|
|
bool bossIsBanished = boss && boss->HasAura(SPELL_NETHERSPITE_BANISHED);
|
|
|
|
if (!boss || !redPortal || bossIsBanished)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (lastBossBanishState[botGuid] != bossIsBanished)
|
|
{
|
|
if (!bossIsBanished)
|
|
{
|
|
beamMoveTimes[botGuid] = 0;
|
|
lastBeamMoveSideways[botGuid] = false;
|
|
}
|
|
lastBossBanishState[botGuid] = bossIsBanished;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Two non-Rogue/Warrior DPS bots will block the blue beam for each phase (swap at 25 debuff stacks)
|
|
// When avoiding void zones, blocking bots will move along the beam to continue blocking
|
|
bool KarazhanNetherspiteBlockBlueBeamAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
std::vector<Player*> blueBlockers = karazhanHelper.GetBlueBlockers();
|
|
static std::map<ObjectGuid, bool> wasBlockingBlueBeam;
|
|
ObjectGuid botGuid = bot->GetGUID();
|
|
Player* assignedBlueBlocker = blueBlockers.empty() ? nullptr : blueBlockers.front();
|
|
bool isBlockingNow = (bot == assignedBlueBlocker);
|
|
bool wasBlocking = wasBlockingBlueBeam[botGuid];
|
|
|
|
if (wasBlocking && !isBlockingNow)
|
|
{
|
|
std::map<std::string, std::string> ph;
|
|
ph["%player"] = bot->GetName();
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"netherspite_beam_leaving_blue", "%player is leaving the blue beam--next blocker up!", ph);
|
|
bot->Yell(text, LANG_UNIVERSAL);
|
|
|
|
wasBlockingBlueBeam[botGuid] = false;
|
|
return false;
|
|
}
|
|
|
|
if (isBlockingNow)
|
|
{
|
|
if (!wasBlocking)
|
|
{
|
|
std::map<std::string, std::string> ph;
|
|
ph["%player"] = bot->GetName();
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"netherspite_beam_blocking_blue", "%player is moving to block the blue beam!", ph);
|
|
bot->Yell(text, LANG_UNIVERSAL);
|
|
}
|
|
wasBlockingBlueBeam[botGuid] = true;
|
|
|
|
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
|
|
float bx = boss->GetPositionX();
|
|
float by = boss->GetPositionY();
|
|
float bz = boss->GetPositionZ();
|
|
float px = bluePortal->GetPositionX();
|
|
float py = bluePortal->GetPositionY();
|
|
float dx = px - bx;
|
|
float dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
if (length == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
dx /= length;
|
|
dy /= length;
|
|
float bestDist = 150.0f;
|
|
Position bestPos;
|
|
bool found = false;
|
|
for (float dist = 18.0f; dist <= 25.0f; dist += 0.5f)
|
|
{
|
|
float candidateX = bx + dx * dist;
|
|
float candidateY = by + dy * dist;
|
|
float candidateZ = bz;
|
|
bool outsideAllVoidZones = true;
|
|
for (Unit* voidZone : voidZones)
|
|
{
|
|
float voidZoneDist = sqrt(pow(candidateX - voidZone->GetPositionX(), 2) +
|
|
pow(candidateY - voidZone->GetPositionY(), 2));
|
|
if (voidZoneDist < 4.0f)
|
|
{
|
|
outsideAllVoidZones = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!outsideAllVoidZones)
|
|
{
|
|
continue;
|
|
}
|
|
float distToIdeal = fabs(dist - 18.0f);
|
|
if (!found || distToIdeal < bestDist)
|
|
{
|
|
bestDist = distToIdeal;
|
|
bestPos = Position(candidateX, candidateY, candidateZ);
|
|
found = true;
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(),
|
|
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
wasBlockingBlueBeam[botGuid] = false;
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanNetherspiteBlockBlueBeamAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
|
|
|
|
return boss && bluePortal && !boss->HasAura(SPELL_NETHERSPITE_BANISHED);
|
|
}
|
|
|
|
// Two healer bots will block the green beam for each phase (swap at 25 debuff stacks)
|
|
// OR one rogue or DPS warrior bot will block the green beam for an entire phase (if they begin the phase as the blocker)
|
|
// When avoiding void zones, blocking bots will move along the beam to continue blocking
|
|
bool KarazhanNetherspiteBlockGreenBeamAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
std::vector<Player*> greenBlockers = karazhanHelper.GetGreenBlockers();
|
|
static std::map<ObjectGuid, bool> wasBlockingGreenBeam;
|
|
ObjectGuid botGuid = bot->GetGUID();
|
|
Player* assignedGreenBlocker = greenBlockers.empty() ? nullptr : greenBlockers.front();
|
|
bool isBlockingNow = (bot == assignedGreenBlocker);
|
|
bool wasBlocking = wasBlockingGreenBeam[botGuid];
|
|
|
|
if (wasBlocking && !isBlockingNow)
|
|
{
|
|
std::map<std::string, std::string> ph;
|
|
ph["%player"] = bot->GetName();
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"netherspite_beam_leaving_green", "%player is leaving the green beam--next blocker up!", ph);
|
|
bot->Yell(text, LANG_UNIVERSAL);
|
|
|
|
wasBlockingGreenBeam[botGuid] = false;
|
|
return false;
|
|
}
|
|
|
|
if (isBlockingNow)
|
|
{
|
|
if (!wasBlocking)
|
|
{
|
|
std::map<std::string, std::string> ph;
|
|
ph["%player"] = bot->GetName();
|
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
|
"netherspite_beam_blocking_green", "%player is moving to block the green beam!", ph);
|
|
bot->Yell(text, LANG_UNIVERSAL);
|
|
}
|
|
wasBlockingGreenBeam[botGuid] = true;
|
|
|
|
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
|
|
float bx = boss->GetPositionX();
|
|
float by = boss->GetPositionY();
|
|
float bz = boss->GetPositionZ();
|
|
float px = greenPortal->GetPositionX();
|
|
float py = greenPortal->GetPositionY();
|
|
float dx = px - bx;
|
|
float dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
if (length == 0.0f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
dx /= length;
|
|
dy /= length;
|
|
float bestDist = 150.0f;
|
|
Position bestPos;
|
|
bool found = false;
|
|
for (float dist = 18.0f; dist <= 25.0f; dist += 0.5f)
|
|
{
|
|
float candidateX = bx + dx * dist;
|
|
float candidateY = by + dy * dist;
|
|
float candidateZ = bz;
|
|
bool outsideAllVoidZones = true;
|
|
for (Unit* voidZone : voidZones)
|
|
{
|
|
float voidZoneDist = sqrt(pow(candidateX - voidZone->GetPositionX(), 2) +
|
|
pow(candidateY - voidZone->GetPositionY(), 2));
|
|
if (voidZoneDist < 4.0f)
|
|
{
|
|
outsideAllVoidZones = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!outsideAllVoidZones)
|
|
{
|
|
continue;
|
|
}
|
|
float distToIdeal = fabs(dist - 18.0f);
|
|
if (!found || distToIdeal < bestDist)
|
|
{
|
|
bestDist = distToIdeal;
|
|
bestPos = Position(candidateX, candidateY, candidateZ);
|
|
found = true;
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(),
|
|
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
wasBlockingGreenBeam[botGuid] = false;
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanNetherspiteBlockGreenBeamAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
|
|
|
|
return boss && greenPortal && !boss->HasAura(SPELL_NETHERSPITE_BANISHED);
|
|
}
|
|
|
|
// All bots not currently blocking a beam will avoid beams and void zones
|
|
bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
auto [redBlocker, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers();
|
|
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
|
|
|
|
bool nearVoidZone = false;
|
|
for (Unit* vz : voidZones)
|
|
{
|
|
if (bot->GetExactDist2d(vz) < 4.0f)
|
|
{
|
|
nearVoidZone = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct BeamAvoid { Unit* portal; float minDist, maxDist; };
|
|
std::vector<BeamAvoid> beams;
|
|
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
|
|
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
|
|
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
|
|
if (redPortal)
|
|
{
|
|
float bx = boss->GetPositionX(), by = boss->GetPositionY();
|
|
float px = redPortal->GetPositionX(), py = redPortal->GetPositionY();
|
|
float dx = px - bx, dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
beams.push_back({redPortal, 0.0f, length});
|
|
}
|
|
if (bluePortal)
|
|
{
|
|
float bx = boss->GetPositionX(), by = boss->GetPositionY();
|
|
float px = bluePortal->GetPositionX(), py = bluePortal->GetPositionY();
|
|
float dx = px - bx, dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
beams.push_back({bluePortal, 0.0f, length});
|
|
}
|
|
if (greenPortal)
|
|
{
|
|
float bx = boss->GetPositionX(), by = boss->GetPositionY();
|
|
float px = greenPortal->GetPositionX(), py = greenPortal->GetPositionY();
|
|
float dx = px - bx, dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
beams.push_back({greenPortal, 0.0f, length});
|
|
}
|
|
bool nearBeam = false;
|
|
for (const auto& beam : beams)
|
|
{
|
|
float bx = boss->GetPositionX(), by = boss->GetPositionY();
|
|
float px = beam.portal->GetPositionX(), py = beam.portal->GetPositionY();
|
|
float dx = px - bx, dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
if (length == 0.0f)
|
|
{
|
|
continue;
|
|
}
|
|
dx /= length; dy /= length;
|
|
float botdx = bot->GetPositionX() - bx, botdy = bot->GetPositionY() - by;
|
|
float t = (botdx * dx + botdy * dy);
|
|
float beamX = bx + dx * t, beamY = by + dy * t;
|
|
float distToBeam = sqrt(pow(bot->GetPositionX() - beamX, 2) + pow(bot->GetPositionY() - beamY, 2));
|
|
if (distToBeam < 5.0f && t > beam.minDist && t < beam.maxDist)
|
|
{
|
|
nearBeam = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!nearVoidZone && !nearBeam)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const float minMoveDist = 3.0f, maxSearchDist = 20.0f, stepAngle = M_PI/18.0f, stepDist = 0.5f;
|
|
float bossZ = boss->GetPositionZ();
|
|
Position bestCandidate;
|
|
float bestDist = 0.0f;
|
|
bool found = false;
|
|
for (float angle = 0; angle < 2 * M_PI; angle += stepAngle)
|
|
{
|
|
for (float dist = 5.0f; dist <= maxSearchDist; dist += stepDist)
|
|
{
|
|
float cx = bot->GetPositionX() + cos(angle) * dist;
|
|
float cy = bot->GetPositionY() + sin(angle) * dist;
|
|
float cz = bossZ;
|
|
if (std::any_of(voidZones.begin(), voidZones.end(), [&](Unit* vz){ return Position(cx, cy, cz).GetExactDist2d(vz) < 4.0f; }))
|
|
{
|
|
continue;
|
|
}
|
|
bool tooCloseToBeam = false;
|
|
for (const auto& beam : beams)
|
|
{
|
|
float bx = boss->GetPositionX(), by = boss->GetPositionY();
|
|
float px = beam.portal->GetPositionX(), py = beam.portal->GetPositionY();
|
|
float dx = px - bx, dy = py - by;
|
|
float length = sqrt(dx*dx + dy*dy);
|
|
if (length == 0.0f)
|
|
{
|
|
continue;
|
|
}
|
|
dx /= length; dy /= length;
|
|
float botdx = cx - bx, botdy = cy - by;
|
|
float t = (botdx * dx + botdy * dy);
|
|
float beamX = bx + dx * t, beamY = by + dy * t;
|
|
float distToBeam = sqrt(pow(cx - beamX, 2) + pow(cy - beamY, 2));
|
|
if (distToBeam < 5.0f && t > beam.minDist && t < beam.maxDist)
|
|
{
|
|
tooCloseToBeam = true;
|
|
break;
|
|
}
|
|
}
|
|
if (tooCloseToBeam)
|
|
{
|
|
continue;
|
|
}
|
|
float moveDist = sqrt(pow(cx - bot->GetPositionX(), 2) + pow(cy - bot->GetPositionY(), 2));
|
|
if (moveDist < minMoveDist)
|
|
{
|
|
continue;
|
|
}
|
|
if (!found || moveDist < bestDist)
|
|
{
|
|
bestCandidate = Position(cx, cy, cz);
|
|
bestDist = moveDist;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
if (found && karazhanHelper.IsSafePosition(bestCandidate.GetPositionX(),
|
|
bestCandidate.GetPositionY(), bestCandidate.GetPositionZ(),
|
|
voidZones, 4.0f))
|
|
{
|
|
return MoveTo(bot->GetMapId(), bestCandidate.GetPositionX(), bestCandidate.GetPositionY(),
|
|
bestCandidate.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanNetherspiteAvoidBeamAndVoidZoneAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
if (!boss || boss->HasAura(SPELL_NETHERSPITE_BANISHED))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
auto [redBlocker, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers();
|
|
if (bot == redBlocker || bot == blueBlocker || bot == greenBlocker)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event event)
|
|
{
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
|
|
|
|
for (Unit* vz : voidZones)
|
|
{
|
|
if (vz->GetEntry() == NPC_VOID_ZONE && bot->GetExactDist2d(vz) < 4.0f)
|
|
{
|
|
return FleePosition(vz->GetPosition(), 4.0f);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
if (!boss || !boss->HasAura(SPELL_NETHERSPITE_BANISHED))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
|
|
for (Unit* vz : voidZones)
|
|
{
|
|
if (bot->GetExactDist2d(vz) < 4.0f)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
std::vector<Unit*> infernals = karazhanHelper.GetSpawnedInfernals();
|
|
|
|
const float minSafeBossDistance = 35.0f;
|
|
const float maxSafeBossDistance = 40.0f;
|
|
const float safeInfernalDistance = 22.0f;
|
|
const float stepSize = 0.5f;
|
|
const int numAngles = 64;
|
|
float bx = bot->GetPositionX();
|
|
float by = bot->GetPositionY();
|
|
float bz = bot->GetPositionZ();
|
|
float bossX = boss->GetPositionX();
|
|
float bossY = boss->GetPositionY();
|
|
float bossZ = boss->GetPositionZ();
|
|
float bestMoveDist = std::numeric_limits<float>::max();
|
|
float bestDestX = 0.0f, bestDestY = 0.0f, bestDestZ = bz;
|
|
bool found = false;
|
|
|
|
if (bot->HasAura(SPELL_ENFEEBLE))
|
|
{
|
|
for (int i = 0; i < numAngles; ++i)
|
|
{
|
|
float angle = (2 * M_PI * i) / numAngles;
|
|
float dx = cos(angle);
|
|
float dy = sin(angle);
|
|
for (float dist = minSafeBossDistance; dist <= maxSafeBossDistance; dist += stepSize)
|
|
{
|
|
float x = bossX + dx * dist;
|
|
float y = bossY + dy * dist;
|
|
float destZ = bossZ;
|
|
if (!bot->IsWithinLOS(x, y, destZ))
|
|
{
|
|
continue;
|
|
}
|
|
bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(x, y, destZ),
|
|
infernals, safeInfernalDistance, stepSize);
|
|
float moveDist = sqrt(pow(x - bx, 2) + pow(y - by, 2));
|
|
if (pathSafe && moveDist < bestMoveDist)
|
|
{
|
|
bestMoveDist = moveDist;
|
|
bestDestX = x;
|
|
bestDestY = y;
|
|
bestDestZ = destZ;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
bot->AttackStop();
|
|
bot->InterruptNonMeleeSpells(false);
|
|
|
|
return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true,
|
|
MovementPriority::MOVEMENT_FORCED);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!bot->HasAura(SPELL_ENFEEBLE))
|
|
{
|
|
bool nearInfernal = false;
|
|
for (Unit* infernal : infernals)
|
|
{
|
|
float infernalDist = sqrt(pow(bx - infernal->GetPositionX(), 2) + pow(by - infernal->GetPositionY(), 2));
|
|
if (infernalDist < safeInfernalDistance)
|
|
{
|
|
nearInfernal = true;
|
|
break;
|
|
}
|
|
}
|
|
if (nearInfernal)
|
|
{
|
|
float bestMoveDist = std::numeric_limits<float>::max();
|
|
float bestDestX = bx, bestDestY = by, bestDestZ = bz;
|
|
bool found = false;
|
|
bool usedArc = false;
|
|
for (int i = 0; i < numAngles; ++i)
|
|
{
|
|
float angle = (2 * M_PI * i) / numAngles;
|
|
float dx = cos(angle);
|
|
float dy = sin(angle);
|
|
for (float dist = stepSize; dist <= 35.0f; dist += stepSize)
|
|
{
|
|
float x = bossX + dx * dist;
|
|
float y = bossY + dy * dist;
|
|
float destZ = bossZ;
|
|
if (!bot->IsWithinLOS(x, y, destZ))
|
|
{
|
|
continue;
|
|
}
|
|
bool pathSafe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(x, y, destZ),
|
|
infernals, safeInfernalDistance, stepSize);
|
|
float moveDist = sqrt(pow(x - bx, 2) + pow(y - by, 2));
|
|
if (pathSafe && moveDist < bestMoveDist)
|
|
{
|
|
bestMoveDist = moveDist;
|
|
bestDestX = x;
|
|
bestDestY = y;
|
|
bestDestZ = destZ;
|
|
found = true;
|
|
usedArc = false;
|
|
}
|
|
if (!pathSafe)
|
|
{
|
|
Position arcPoint = karazhanHelper.CalculateArcPoint(Position(bx, by, bz), Position(x, y, destZ),
|
|
Position(bossX, bossY, bossZ));
|
|
if (!bot->IsWithinLOS(arcPoint.GetPositionX(), arcPoint.GetPositionY(), arcPoint.GetPositionZ()))
|
|
{
|
|
continue;
|
|
}
|
|
bool arcSafe = true;
|
|
for (Unit* infernal : infernals)
|
|
{
|
|
float infernalDist = sqrt(pow(arcPoint.GetPositionX() - infernal->GetPositionX(), 2) +
|
|
pow(arcPoint.GetPositionY() - infernal->GetPositionY(), 2));
|
|
if (infernalDist < safeInfernalDistance)
|
|
{
|
|
arcSafe = false;
|
|
break;
|
|
}
|
|
}
|
|
float arcMoveDist = sqrt(pow(arcPoint.GetPositionX() - bx, 2) +
|
|
pow(arcPoint.GetPositionY() - by, 2));
|
|
if (arcSafe && arcMoveDist < bestMoveDist)
|
|
{
|
|
bestMoveDist = arcMoveDist;
|
|
bestDestX = arcPoint.GetPositionX();
|
|
bestDestY = arcPoint.GetPositionY();
|
|
bestDestZ = arcPoint.GetPositionZ();
|
|
found = true;
|
|
usedArc = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
bot->AttackStop();
|
|
bot->InterruptNonMeleeSpells(false);
|
|
|
|
return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true,
|
|
MovementPriority::MOVEMENT_COMBAT);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanPrinceMalchezaarNonTankAvoidHazardAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
|
|
|
return boss && !(botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot);
|
|
}
|
|
|
|
bool KarazhanPrinceMalchezaarTankAvoidHazardAction::Execute(Event event)
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
|
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
std::vector<Unit*> infernals = karazhanHelper.GetSpawnedInfernals();
|
|
|
|
const float safeInfernalDistance = 30.0f;
|
|
const float stepSize = 0.5f;
|
|
const int numAngles = 64;
|
|
const float maxSampleDist = 60.0f;
|
|
float bx = bot->GetPositionX();
|
|
float by = bot->GetPositionY();
|
|
float bz = bot->GetPositionZ();
|
|
float bestMoveDist = std::numeric_limits<float>::max();
|
|
float bestDestX = bx, bestDestY = by, bestDestZ = bz;
|
|
bool found = false;
|
|
bool usedArc = false;
|
|
|
|
bool nearInfernal = false;
|
|
for (Unit* infernal : infernals)
|
|
{
|
|
float infernalDist = sqrt(pow(bx - infernal->GetPositionX(), 2) + pow(by - infernal->GetPositionY(), 2));
|
|
if (infernalDist < safeInfernalDistance)
|
|
{
|
|
nearInfernal = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nearInfernal)
|
|
{
|
|
for (int i = 0; i < numAngles; ++i)
|
|
{
|
|
float angle = (2 * M_PI * i) / numAngles;
|
|
float dx = cos(angle);
|
|
float dy = sin(angle);
|
|
for (float dist = stepSize; dist <= maxSampleDist; dist += stepSize)
|
|
{
|
|
float x = bx + dx * dist;
|
|
float y = by + dy * dist;
|
|
float z = bz;
|
|
if (!bot->IsWithinLOS(x, y, z))
|
|
{
|
|
continue;
|
|
}
|
|
bool safe = karazhanHelper.IsStraightPathSafe(Position(bx, by, bz), Position(x, y, z),
|
|
infernals, safeInfernalDistance, stepSize);
|
|
float moveDist = sqrt(pow(x - bx, 2) + pow(y - by, 2));
|
|
if (safe && moveDist < bestMoveDist)
|
|
{
|
|
bestMoveDist = moveDist;
|
|
bestDestX = x;
|
|
bestDestY = y;
|
|
bestDestZ = z;
|
|
found = true;
|
|
usedArc = false;
|
|
}
|
|
if (!safe)
|
|
{
|
|
Position arcPoint = karazhanHelper.CalculateArcPoint(Position(bx, by, bz), Position(x, y, z),
|
|
Position(bx, by, bz));
|
|
float arcX = arcPoint.GetPositionX();
|
|
float arcY = arcPoint.GetPositionY();
|
|
float arcZ = arcPoint.GetPositionZ();
|
|
float arcDestX = arcX, arcDestY = arcY, arcDestZ = arcZ;
|
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bx, by, bz, arcDestX, arcDestY, arcDestZ))
|
|
{
|
|
continue;
|
|
}
|
|
bool arcSafe = true;
|
|
for (Unit* infernal : infernals)
|
|
{
|
|
float infernalDist = sqrt(pow(arcPoint.GetPositionX() - infernal->GetPositionX(), 2) +
|
|
pow(arcPoint.GetPositionY() - infernal->GetPositionY(), 2));
|
|
if (infernalDist < safeInfernalDistance)
|
|
{
|
|
arcSafe = false;
|
|
break;
|
|
}
|
|
}
|
|
float arcMoveDist = sqrt(pow(arcPoint.GetPositionX() - bx, 2) + pow(arcPoint.GetPositionY() - by, 2));
|
|
if (arcSafe && arcMoveDist < bestMoveDist)
|
|
{
|
|
bestMoveDist = arcMoveDist;
|
|
bestDestX = arcPoint.GetPositionX();
|
|
bestDestY = arcPoint.GetPositionY();
|
|
bestDestZ = arcPoint.GetPositionZ();
|
|
found = true;
|
|
usedArc = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
bot->AttackStop();
|
|
bot->InterruptNonMeleeSpells(false);
|
|
|
|
return MoveTo(bot->GetMapId(), bestDestX, bestDestY, bestDestZ, false, false, false, true,
|
|
MovementPriority::MOVEMENT_COMBAT);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool KarazhanPrinceMalchezaarTankAvoidHazardAction::isUseful()
|
|
{
|
|
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
|
|
|
return boss && botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot;
|
|
}
|