#include "Playerbots.h" #include "PitOfSaronActions.h" #include "PitOfSaronStrategy.h" #include "SharedDefines.h" bool IckAndKrickAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "Ick"); if (!boss) return false; std::vector orbs; bool orb = false; // First gather all orbs GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs1) { Unit* unit = botAI->GetUnit(npc); if (unit && unit->IsAlive() && unit->HasAura(SPELL_EXPLODING_ORB_VISUAL)) { orb = true; break; } } bool pursuit = bot->HasAura(SPELL_PURSUIT) || (!botAI->IsTank(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_PURSUIT)); bool poisonNova = boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC)); bool explosiveBarrage = orb || boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK)); bool isTank = botAI->IsTank(bot); if (pursuit && Pursuit(pursuit, boss)) return true; if (poisonNova && PoisonNova(poisonNova, boss)) return true; if (explosiveBarrage && ExplosiveBarrage(explosiveBarrage, boss)) return true; if (isTank && !pursuit && !poisonNova && !explosiveBarrage) { if (TankPosition(boss)) return true; } return false; } bool IckAndKrickAction::TankPosition(Unit* boss) { if (botAI->HasAggro(boss)) { float distance = bot->GetExactDist2d(ICKANDKRICK_TANK_POSITION.GetPositionX(), ICKANDKRICK_TANK_POSITION.GetPositionY()); if (distance > 7.0f) { // Calculate direction vector float dirX = ICKANDKRICK_TANK_POSITION.GetPositionX() - bot->GetPositionX(); float dirY = ICKANDKRICK_TANK_POSITION.GetPositionY() - bot->GetPositionY(); float length = sqrt(dirX * dirX + dirY * dirY); dirX /= length; dirY /= length; // Move in increments of 3.0f float moveX = bot->GetPositionX() + dirX * 3.0f; float moveY = bot->GetPositionY() + dirY * 3.0f; return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } } return false; } bool IckAndKrickAction::Pursuit(bool pursuit, Unit* boss) { // Only execute this action when pursuit is active and for non-tank players if (!pursuit || botAI->IsTank(bot)) return false; // Get the tank position as a reference point Position tankPosition = ICKANDKRICK_TANK_POSITION; // Calculate distance from boss float distanceToBoss = bot->GetExactDist2d(boss); // If boss is close, move in a circular pattern if (distanceToBoss < 20.0f) { // Current bot position float x = bot->GetPositionX(); float y = bot->GetPositionY(); // Calculate vector from tank to bot float dx = x - tankPosition.GetPositionX(); float dy = y - tankPosition.GetPositionY(); // Normalize the vector float distance = std::sqrt(dx * dx + dy * dy); if (distance > 0) { dx /= distance; dy /= distance; } // Rotate the vector by 90 degrees to create circular movement // (x,y) rotated 90 degrees becomes (-y,x) float rotatedX = -dy; float rotatedY = dx; // Create a target position along the circular path // We want to maintain a specific radius from the tank float targetRadius = 20.0f; // 20 feet radius circle around tank Position targetPos; targetPos.Relocate(tankPosition.GetPositionX() + rotatedX * targetRadius, tankPosition.GetPositionY() + rotatedY * targetRadius, bot->GetPositionZ()); // Move to the calculated circular position botAI->Reset(); return MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } return false; } bool IckAndKrickAction::PoisonNova(bool poisonNova, Unit* boss) { if (bot->GetExactDist2d(boss) < 20.0f && poisonNova) return MoveAway(boss, 11.0f); else if (poisonNova) return true; return false; } bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) { std::vector orbs; Unit* closestOrb = nullptr; float closestDistance = std::numeric_limits::max(); // First gather all orbs GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs1) { Unit* unit = botAI->GetUnit(npc); if (unit && unit->IsAlive() && unit->HasAura(SPELL_EXPLODING_ORB_VISUAL)) { orbs.push_back(unit); float dist = bot->GetDistance(unit); if (dist < closestDistance) { closestDistance = dist; closestOrb = unit; } } } if (!orbs.empty() && closestOrb && bot->GetExactDist2d(closestOrb) < 7.0f) { // Get player positions to avoid moving toward them std::vector playerPositions; Group* group = bot->GetGroup(); if (group) { for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { if (Player* member = ref->GetSource()) { if (member != bot && member->IsAlive() && bot->GetMap() == member->GetMap() && bot->GetExactDist2d(member) < 20.0f) { playerPositions.push_back(member->GetPosition()); } } } } // Calculate several potential escape positions const int NUM_ANGLES = 16; // Check 16 different directions const float SAFE_DISTANCE = 7.0f; const float MAX_BOSS_DISTANCE = 30.0f; // Maximum allowed distance from boss const float ANGLE_INCREMENT = 2 * M_PI / NUM_ANGLES; Position bestPos; bool foundValidPos = false; float bestScore = -1.0f; for (int i = 0; i < NUM_ANGLES; i++) { float angle = i * ANGLE_INCREMENT; Position potentialPos; potentialPos.m_positionX = bot->GetPositionX() + SAFE_DISTANCE * cos(angle); potentialPos.m_positionY = bot->GetPositionY() + SAFE_DISTANCE * sin(angle); potentialPos.m_positionZ = bot->GetPositionZ(); // Check if position is valid (not in a wall) if (!bot->IsWithinLOS(potentialPos.m_positionX, potentialPos.m_positionY, potentialPos.m_positionZ)) continue; // Check if position is within maximum allowed distance from boss if (boss && sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) + pow(potentialPos.m_positionY - boss->GetPositionY(), 2)) > MAX_BOSS_DISTANCE) continue; // Score this position based on: // 1. Distance from all orbs (farther is better) // 2. Distance from other players (farther is better) // 3. Distance from boss (closer is better, but still safe) float score = 0.0f; // Check distance from all orbs float minOrbDist = std::numeric_limits::max(); for (Unit* orb : orbs) { float orbDist = sqrt(pow(potentialPos.m_positionX - orb->GetPositionX(), 2) + pow(potentialPos.m_positionY - orb->GetPositionY(), 2)); minOrbDist = std::min(minOrbDist, orbDist); } score += minOrbDist * 2.0f; // Weight orb distance more heavily // Check distance from other players for (const Position& playerPos : playerPositions) { float playerDist = sqrt(pow(potentialPos.m_positionX - playerPos.m_positionX, 2) + pow(potentialPos.m_positionY - playerPos.m_positionY, 2)); score += std::min(playerDist, 10.0f); // Cap player distance contribution } // Factor in proximity to boss (closer is better, as long as we're safe from orbs) if (boss) { float bossDist = sqrt(pow(potentialPos.m_positionX - boss->GetPositionX(), 2) + pow(potentialPos.m_positionY - boss->GetPositionY(), 2)); // Add points for being closer to boss (inverse relationship) // but only if we're safely away from orbs if (minOrbDist > SAFE_DISTANCE) { score += (MAX_BOSS_DISTANCE - bossDist) * 0.5f; } } // If this is the best position so far, remember it if (score > bestScore) { bestScore = score; bestPos = potentialPos; foundValidPos = true; } } // If we found a valid position, move there if (foundValidPos) { botAI->Reset(); // Use MoveTo instead of FleePosition since we already calculated the exact position return MoveTo(bot->GetMapId(), bestPos.m_positionX, bestPos.m_positionY, bot->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } else { return FleePosition(closestOrb->GetPosition(), 7.0f, 250U); } } return false; } bool TyrannusAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus"); if (!boss) return false; bool rangedSpread = false; if (botAI->IsRanged(bot) && boss->HealthBelowPct(99)) rangedSpread = true; if (rangedSpread && RangedSpread(rangedSpread)) return true; return false; } bool TyrannusAction::RangedSpread(bool rangedSpread) { float radius = 10.0f; float moveIncrement = 3.0f; GuidVector members = AI_VALUE(GuidVector, "group members"); if (botAI->IsRanged(bot) && rangedSpread) { // Ranged: spread from other members for (auto& member : members) { Unit* unit = botAI->GetUnit(member); if (!unit || !unit->IsAlive() || unit == bot) continue; float dist = bot->GetExactDist2d(unit); if (dist < radius) { float moveDistance = std::min(moveIncrement, radius - dist + 1.0f); return FleePosition(unit->GetPosition(), moveDistance, 250U); } } } return false; }