diff --git a/src/strategy/raids/icecrown/RaidIccActions.cpp b/src/strategy/raids/icecrown/RaidIccActions.cpp index 8d79c184..137a4b2c 100644 --- a/src/strategy/raids/icecrown/RaidIccActions.cpp +++ b/src/strategy/raids/icecrown/RaidIccActions.cpp @@ -708,18 +708,20 @@ bool IccRotfaceTankPositionAction::Execute(Event event) if (bot->GetExactDist2d(ICC_ROTFACE_TANK_POSITION) > 7.0f) return MoveTo(bot->GetMapId(), ICC_ROTFACE_TANK_POSITION.GetPositionX(), ICC_ROTFACE_TANK_POSITION.GetPositionY(), ICC_ROTFACE_TANK_POSITION.GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED); + false, false, true, MovementPriority::MOVEMENT_COMBAT); } + bool hasOozeFlood = botAI->HasAura("Ooze Flood", bot); + // Assist tank positioning for big ooze if (botAI->IsAssistTank(bot)) { // If we have the ooze flood aura, move away - if (bot->HasAura(71215)) + if (hasOozeFlood) { return MoveTo(boss->GetMapId(), boss->GetPositionX() + 5.0f * cos(bot->GetAngle(boss)), boss->GetPositionY() + 5.0f * sin(bot->GetAngle(boss)), bot->GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED); + false, false, true, MovementPriority::MOVEMENT_COMBAT); } Unit* bigOoze = AI_VALUE2(Unit*, "find target", "big ooze"); @@ -740,7 +742,7 @@ bool IccRotfaceTankPositionAction::Execute(Event event) { return MoveTo(bot->GetMapId(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionX(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionY(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_FORCED); + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } return Attack(bigOoze); } @@ -763,6 +765,7 @@ bool IccRotfaceGroupPositionAction::Execute(Event event) // Check for puddles and move away if too close GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + bool hasOozeFlood = botAI->HasAura("Ooze Flood", bot); for (auto& npc : npcs) { @@ -774,7 +777,7 @@ bool IccRotfaceGroupPositionAction::Execute(Event event) float puddleDistance = bot->GetExactDist2d(unit); - if (puddleDistance < 30.0f && (bot->HasAura(71215) || bot->HasAura(69789))) + if (puddleDistance < 30.0f && (hasOozeFlood)) { float dx = boss->GetPositionX() - unit->GetPositionX(); float dy = boss->GetPositionY() - unit->GetPositionY(); @@ -786,7 +789,7 @@ bool IccRotfaceGroupPositionAction::Execute(Event event) float moveY = boss->GetPositionY() + (moveDistance * sin(angle)); return MoveTo(boss->GetMapId(), moveX, moveY, boss->GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_FORCED); + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } } } @@ -794,20 +797,22 @@ bool IccRotfaceGroupPositionAction::Execute(Event event) // Check if we're targeted by little ooze Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); - if (smallOoze && smallOoze->GetVictim() == bot) + bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot); + + if ((smallOoze && smallOoze->GetVictim() == bot) || hasMutatedInfection) { if (bot->GetExactDist2d(ICC_ROTFACE_BIG_OOZE_POSITION) > 3.0f) { return MoveTo(bot->GetMapId(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionX(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionY(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_FORCED); + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } return true; // Stay at position } if(botAI->IsRanged(bot) || botAI->IsHeal(bot)) { - if (!bot->HasAura(71215) && !bot->HasAura(69789)) // ooze flood id + if (!hasOozeFlood) { float radius = 10.0f; Unit* closestMember = nullptr; @@ -865,55 +870,140 @@ bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event event) // Move to the position return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, - false, false, false, true, MovementPriority::MOVEMENT_FORCED); + false, false, false, false, MovementPriority::MOVEMENT_FORCED); } //PP bool IccPutricideGrowingOozePuddleAction::Execute(Event event) { - const float radius = 12.0f; + const float BASE_RADIUS = 2.0f; + const float STACK_MULTIPLIER = 0.5f; + const float MIN_DISTANCE = 0.1f; // Minimum distance to consider when bot is very close or inside puddle // Get the nearest hostile NPCs GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + float closestDistance = FLT_MAX; + Unit* closestPuddle = nullptr; + float closestSafeDistance = BASE_RADIUS; + + // Find the closest puddle and its safe distance for (auto& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (unit && unit->GetEntry() == 37690) //growing ooze puddle ID { - float currentDistance = bot->GetDistance2d(unit); - // Move away from the puddle if the bot is too close - if (currentDistance < radius) + // Use GetExactDist instead of GetDistance2d to handle Z-axis + float currentDistance = std::max(MIN_DISTANCE, bot->GetExactDist(unit)); + if (currentDistance < closestDistance) { - return MoveAway(unit, radius - currentDistance); + closestDistance = currentDistance; + closestPuddle = unit; + + // Calculate safe distance for this puddle + if (Aura* grow = unit->GetAura(70347)) + { + closestSafeDistance = BASE_RADIUS + (grow->GetStackAmount() * STACK_MULTIPLIER); + } } } } + + // If we found a puddle that's too close, move away from it + if (closestPuddle && closestDistance < closestSafeDistance) + { + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + float botZ = bot->GetPositionZ(); + + // Calculate vector from puddle to bot + float dx = botX - closestPuddle->GetPositionX(); + float dy = botY - closestPuddle->GetPositionY(); + float dist = std::max(MIN_DISTANCE, sqrt(dx * dx + dy * dy)); + + // If we're too close or inside, pick a random direction to move + if (dist < MIN_DISTANCE * 2) + { + float randomAngle = float(rand()) / float(RAND_MAX) * 2 * M_PI; + dx = cos(randomAngle); + dy = sin(randomAngle); + } + else + { + dx /= dist; + dy /= dist; + } + + // Try different angles to find a safe path + const int numAngles = 8; + float bestMoveX = botX; + float bestMoveY = botY; + bool foundPath = false; + float moveDistance = closestSafeDistance - closestDistance + 2.0f; // Add 2 yards buffer + + for (int i = 0; i < numAngles; i++) + { + float angle = (2 * M_PI * i) / numAngles; + float rotatedDx = dx * cos(angle) - dy * sin(angle); + float rotatedDy = dx * sin(angle) + dy * cos(angle); + + float testX = botX + rotatedDx * moveDistance; + float testY = botY + rotatedDy * moveDistance; + float testZ = botZ; + + // Check if this move would put us too close to any other puddle + bool tooCloseToOtherPuddle = false; + for (auto& otherNpc : npcs) + { + Unit* otherUnit = botAI->GetUnit(otherNpc); + if (otherUnit && otherUnit->GetEntry() == 37690 && otherUnit != closestPuddle) + { + float otherSafeDistance = BASE_RADIUS; + if (Aura* grow = otherUnit->GetAura(70347)) + { + otherSafeDistance = BASE_RADIUS + (grow->GetStackAmount() * STACK_MULTIPLIER); + } + + float newDist = sqrt(pow(testX - otherUnit->GetPositionX(), 2) + + pow(testY - otherUnit->GetPositionY(), 2)); + if (newDist < otherSafeDistance) + { + tooCloseToOtherPuddle = true; + break; + } + } + } + + if (!tooCloseToOtherPuddle && bot->IsWithinLOS(testX, testY, testZ)) + { + bestMoveX = testX; + bestMoveY = testY; + foundPath = true; + break; + } + } + + if (foundPath) + { + return MoveTo(bot->GetMapId(), bestMoveX, bestMoveY, botZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } return false; } bool IccPutricideVolatileOozeAction::Execute(Event event) { - const float POSITION_TOLERANCE = 5.0f; const float STACK_DISTANCE = 8.0f; - const float SAFE_DISTANCE = 20.0f; - - // 1. Main tank positioning - if (botAI->IsMainTank(bot)) - return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_OOZE_POSITION.GetPositionX(), - ICC_PUTRICIDE_TANK_OOZE_POSITION.GetPositionY(), - ICC_PUTRICIDE_TANK_OOZE_POSITION.GetPositionZ(), - false, true, false, true, MovementPriority::MOVEMENT_NORMAL); // Find the ooze Unit* ooze = AI_VALUE2(Unit*, "find target", "volatile ooze"); - - // If bot is melee (and not main tank), always attack ooze if it exists - if (botAI->IsMelee(bot) && ooze) - { - bot->SetTarget(ooze->GetGUID()); - return Attack(ooze); - } + bool botHasAura = botAI->HasAura("Volatile Ooze Adhesive", bot); + bool botHasAura2 = botAI->HasAura("Gaseous Bloat", bot); + bool botHasAura3 = botAI->HasAura("Unbound Plague", bot); + + if (botHasAura2 || botHasAura3) + return false; // Check for aura on any group member Group* group = bot->GetGroup(); @@ -921,60 +1011,80 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) return false; Unit* auraTarget = nullptr; + Unit* stackTarget = nullptr; bool anyoneHasAura = false; - bool botHasAura = bot->HasAura(70447); // Check if this bot has the aura + // First, try to find someone with the aura for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (!member) + if (!member || !member->IsAlive() || member == bot) continue; - if (member->HasAura(70447)) // Volatile Ooze Adhesive + if (botAI->HasAura("Volatile Ooze Adhesive", member)) { anyoneHasAura = true; auraTarget = member; + stackTarget = member; break; } } + // If no one has aura, find a ranged player to stack with + if (!anyoneHasAura && !stackTarget) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot || + botAI->IsTank(member) || botAI->HasAura("Gaseous Bloat", member) || + botAI->HasAura("Unbound Plague", member)) + continue; + + if (botAI->IsRanged(member)) + { + stackTarget = member; + break; + } + } + } + + // For melee + if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot)) + { + // If ooze exists and someone has aura, attack the ooze + if (ooze && anyoneHasAura) + { + bot->SetTarget(ooze->GetGUID()); + return Attack(ooze); + } + // Otherwise stack with ranged + else if (stackTarget) + { + if (bot->GetDistance2d(stackTarget) > STACK_DISTANCE) + { + return MoveTo(bot->GetMapId(), stackTarget->GetPositionX(), + stackTarget->GetPositionY(), stackTarget->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + } + // For ranged and healers if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) { - // If bot has aura or someone else has aura, stack with aura target - if (botHasAura || (anyoneHasAura && auraTarget)) + // Always try to stack + if (stackTarget && bot->GetDistance2d(stackTarget) > STACK_DISTANCE) { - Position targetPos; - targetPos.m_positionX = auraTarget->GetPositionX(); - targetPos.m_positionY = auraTarget->GetPositionY(); - targetPos.m_positionZ = auraTarget->GetPositionZ(); - - if (bot->GetExactDist2d(targetPos) > STACK_DISTANCE) - { - bot->AttackStop(); - return MoveTo(bot->GetMapId(), targetPos.GetPositionX(), - targetPos.GetPositionY(), targetPos.GetPositionZ(), - false, true, false, true, MovementPriority::MOVEMENT_NORMAL); - } - } - // If no one has aura and ooze exists, maintain safe distance - else if (ooze) - { - float currentDist = bot->GetExactDist2d(ooze); - if (abs(currentDist - SAFE_DISTANCE) > POSITION_TOLERANCE) - { - // Calculate position 20 yards from ooze - float angle = ooze->GetAngle(bot); - float moveX = ooze->GetPositionX() + (SAFE_DISTANCE * cos(angle)); - float moveY = ooze->GetPositionY() + (SAFE_DISTANCE * sin(angle)); - - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), - false, true, false, true, MovementPriority::MOVEMENT_NORMAL); - } + bot->AttackStop(); + return MoveTo(bot->GetMapId(), stackTarget->GetPositionX(), + stackTarget->GetPositionY(), stackTarget->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } - // If in position and ooze exists, attack it (except healers) - if (ooze && !botAI->IsHeal(bot)) + // If stacked and ooze exists, attack it (except healers) + if (ooze && !botAI->IsHeal(bot) && stackTarget && + bot->GetDistance2d(stackTarget) <= STACK_DISTANCE) { bot->SetTarget(ooze->GetGUID()); return Attack(ooze); @@ -988,106 +1098,98 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) return false; } -uint8_t IccPutricideGasCloudAction::lastKnownPosition = 0; - bool IccPutricideGasCloudAction::Execute(Event event) { if (botAI->IsMainTank(bot)) return false; - // Find the gas cloud Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); if (!gasCloud) return false; - static ObjectGuid lastGasCloudGuid; - // If this is a new gas cloud, reset to position 2 - if (lastGasCloudGuid != gasCloud->GetGUID()) - { - lastGasCloudGuid = gasCloud->GetGUID(); - lastKnownPosition = 0; // This will make it go to position 2 - } - - // Check if this bot has Gaseous Bloat bool botHasAura = botAI->HasAura("Gaseous Bloat", bot); + Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); - // If bot has aura, handle movement between positions + if(!botHasAura && volatileOoze) + return false; + if (botHasAura) { - // Get current position - float dist1 = bot->GetExactDist2d(ICC_PUTRICIDE_GAS1_POSITION.GetPositionX(), ICC_PUTRICIDE_GAS1_POSITION.GetPositionY()); - float dist2 = bot->GetExactDist2d(ICC_PUTRICIDE_GAS2_POSITION.GetPositionX(), ICC_PUTRICIDE_GAS2_POSITION.GetPositionY()); - float dist3 = bot->GetExactDist2d(ICC_PUTRICIDE_GAS3_POSITION.GetPositionX(), ICC_PUTRICIDE_GAS3_POSITION.GetPositionY()); - float dist4 = bot->GetExactDist2d(ICC_PUTRICIDE_GAS4_POSITION.GetPositionX(), ICC_PUTRICIDE_GAS4_POSITION.GetPositionY()); - - uint8_t currentPosition = 0; - const Position* nextPos = nullptr; + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + float botZ = bot->GetPositionZ(); - // Determine current position - if (dist1 < 8.0f) currentPosition = 1; - else if (dist2 < 8.0f) currentPosition = 2; - else if (dist3 < 8.0f) currentPosition = 3; - else if (dist4 < 8.0f) currentPosition = 4; - - // If we're at a new position, update last known position - if (currentPosition != 0 && currentPosition != lastKnownPosition) + float cloudX = gasCloud->GetPositionX(); + float cloudY = gasCloud->GetPositionY(); + float cloudDist = gasCloud->GetExactDist2d(botX, botY); + + // Only move if cloud is close enough to be dangerous + if (cloudDist < 25.0f) { - lastKnownPosition = currentPosition; - } - - // Only prevent movement if we're already moving and haven't reached the target yet - if (lastKnownPosition != 0 && lastKnownPosition != currentPosition && AI_VALUE(bool, "moving")) - { - return false; - } - - // Determine next position to move to - if (gasCloud->GetExactDist2d(bot) < 18.0f) - { - switch(currentPosition) + // Calculate vector from cloud to bot + float dx = botX - cloudX; + float dy = botY - cloudY; + float dist = sqrt(dx * dx + dy * dy); + + if (dist > 0) { - case 0: // Not at any position - case 1: - nextPos = &ICC_PUTRICIDE_GAS2_POSITION; - lastKnownPosition = 2; - break; - case 2: - nextPos = &ICC_PUTRICIDE_GAS3_POSITION; - lastKnownPosition = 3; - break; - case 3: - nextPos = &ICC_PUTRICIDE_GAS4_POSITION; - lastKnownPosition = 4; - break; - case 4: - nextPos = &ICC_PUTRICIDE_GAS1_POSITION; - lastKnownPosition = 1; - break; - } + dx /= dist; + dy /= dist; - if (nextPos) - { - // Use PathGenerator to find a safe path to the target - PathGenerator path(bot); - path.CalculatePath(nextPos->GetPositionX(), nextPos->GetPositionY(), nextPos->GetPositionZ(), false); + // Try different angles to find a safe path + const int numAngles = 16; // Increased for more precise movement + float bestMoveX = botX; + float bestMoveY = botY; + float bestDist = cloudDist; + bool foundPath = false; + + for (int i = 0; i < numAngles; i++) + { + float angle = (2 * M_PI * i) / numAngles; + float rotatedDx = dx * cos(angle) - dy * sin(angle); + float rotatedDy = dx * sin(angle) + dy * cos(angle); - if (path.GetPathType() == PATHFIND_NORMAL || path.GetPathType() == PATHFIND_INCOMPLETE) - { - return MoveTo(bot->GetMapId(), nextPos->GetPositionX(), nextPos->GetPositionY(), nextPos->GetPositionZ(), - false, true, false, false, MovementPriority::MOVEMENT_FORCED); + // Try different distances + for (float testDist = 5.0f; testDist <= 15.0f; testDist += 5.0f) + { + float testX = botX + rotatedDx * testDist; + float testY = botY + rotatedDy * testDist; + float testZ = botZ; + + float newCloudDist = gasCloud->GetExactDist2d(testX, testY); + + // Check if this position is better + if (newCloudDist > bestDist && bot->IsWithinLOS(testX, testY, testZ)) + { + bestMoveX = testX; + bestMoveY = testY; + bestDist = newCloudDist; + foundPath = true; + } + } } - else + + if (foundPath) { - // If no valid path found, try to move directly (old behavior) - return MoveTo(bot->GetMapId(), nextPos->GetPositionX(), nextPos->GetPositionY(), nextPos->GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_FORCED); + return MoveTo(bot->GetMapId(), bestMoveX, bestMoveY, botZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + else if (cloudDist < 8.0f) // Emergency move if very close and no good path found + { + // Try to move directly away + float emergencyX = botX + dx * 10.0f; + float emergencyY = botY + dy * 10.0f; + + if (bot->IsWithinLOS(emergencyX, emergencyY, botZ)) + { + return MoveTo(bot->GetMapId(), emergencyX, emergencyY, botZ, + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } } - } - return false; } - return false; } - // If bot doesn't have aura, check if anyone else does + else { Group* group = bot->GetGroup(); @@ -1108,20 +1210,9 @@ bool IccPutricideGasCloudAction::Execute(Event event) } } - // If someone has aura but not this bot, attack gas cloud (except healers) if (someoneHasAura && !botAI->IsHeal(bot)) - { - lastKnownPosition = 0; - return Attack(gasCloud); - } - // If no one has aura yet, everyone stays at position 2 - else if (!someoneHasAura) - { - lastKnownPosition = 0; - return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_GAS2_POSITION.GetPositionX(), - ICC_PUTRICIDE_GAS2_POSITION.GetPositionY(), - ICC_PUTRICIDE_GAS2_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + { + return Attack(gasCloud); } } @@ -1130,16 +1221,68 @@ bool IccPutricideGasCloudAction::Execute(Event event) bool AvoidMalleableGooAction::Execute(Event event) { + + bool hasUnboundPlague = botAI->HasAura("Unbound Plague", bot); + const float UNBOUND_PLAGUE_DISTANCE = 15.0f; + + // If bot has unbound plague, keep away from all other players + if (hasUnboundPlague) + { + Group* group = bot->GetGroup(); + if (!group) + return false; + + float closestDistance = UNBOUND_PLAGUE_DISTANCE; + Unit* closestPlayer = nullptr; + + // Find closest player + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + float dist = bot->GetDistance2d(member); + if (dist < closestDistance) + { + closestDistance = dist; + closestPlayer = member; + } + } + + // Move away from closest player if too close + if (closestPlayer) + { + float dx = bot->GetPositionX() - closestPlayer->GetPositionX(); + float dy = bot->GetPositionY() - closestPlayer->GetPositionY(); + float dist = sqrt(dx * dx + dy * dy); + + if (dist > 0) + { + dx /= dist; + dy /= dist; + float moveDistance = UNBOUND_PLAGUE_DISTANCE - closestDistance + 2.0f; + + float moveX = bot->GetPositionX() + dx * moveDistance; + float moveY = bot->GetPositionY() + dy * moveDistance; + + if (bot->IsWithinLOS(moveX, moveY, bot->GetPositionZ())) + { + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + } + } + if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) { float radius = 7.0f; - float moveIncrement = 3.0f; bool isRanged = botAI->IsRanged(bot); - GuidVector members = AI_VALUE(GuidVector, "group members"); + GuidVector members = AI_VALUE(GuidVector, "group members"); if (isRanged) { - // Ranged: spread from other members for (auto& member : members) { Unit* unit = botAI->GetUnit(member); @@ -1149,8 +1292,18 @@ bool AvoidMalleableGooAction::Execute(Event event) float dist = bot->GetExactDist2d(unit); if (dist < radius) { - float moveDistance = std::min(moveIncrement, radius - dist + 1.0f); - return MoveAway(unit, moveDistance); + float moveDistance = radius - dist + 1.0f; + + // Calculate potential new position + float angle = bot->GetAngle(unit); + float newX = bot->GetPositionX() + cos(angle + M_PI) * moveDistance; + float newY = bot->GetPositionY() + sin(angle + M_PI) * moveDistance; + + // Only move if we have line of sight + if (bot->IsWithinLOS(newX, newY, bot->GetPositionZ())) + { + return MoveAway(unit, moveDistance); + } } } } @@ -1323,7 +1476,7 @@ bool IccBpcMainTankAction::Execute(Event event) bool IccBpcEmpoweredVortexAction::Execute(Event event) { // Double check that we're not a tank - if (botAI->IsMainTank(bot) || botAI->IsAssistTank(bot)) + if (botAI->IsMainTank(bot) || botAI->IsAssistTank(bot) || botAI->IsTank(bot)) return false; Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); @@ -1331,65 +1484,28 @@ bool IccBpcEmpoweredVortexAction::Execute(Event event) return false; float radius = 12.0f; - float moveIncrement = 3.0f; - bool isRanged = botAI->IsRanged(bot); - GuidVector members = AI_VALUE(GuidVector, "group members"); - if (isRanged) + + for (auto& member : members) { - // Ranged: spread from other ranged - for (auto& member : members) - { - Unit* unit = botAI->GetUnit(member); - if (!unit || !unit->IsAlive() || unit == bot || - botAI->IsMainTank(bot) || botAI->IsAssistTank(bot) || !botAI->IsRanged(bot)) - continue; + 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 MoveAway(unit, moveDistance); - } - } - } - else - { - // Melee: move opposite to ranged group - float avgX = 0, avgY = 0; - int rangedCount = 0; - - for (auto& member : members) - { - Unit* unit = botAI->GetUnit(member); - if (!unit || !unit->IsAlive() || !botAI->IsRanged(bot)) - continue; - - avgX += unit->GetPositionX(); - avgY += unit->GetPositionY(); - rangedCount++; - } - - if (rangedCount > 0) - { - avgX /= rangedCount; - avgY /= rangedCount; - - // Direction from ranged to Valanar - float dx = valanar->GetPositionX() - avgX; - float dy = valanar->GetPositionY() - avgY; - float len = sqrt(dx*dx + dy*dy); + float dist = bot->GetExactDist2d(unit); + if (dist < radius) + { + float moveDistance = radius - dist + 1.0f; - if (len > 0) + // Calculate potential new position + float angle = bot->GetAngle(unit); + float newX = bot->GetPositionX() + cos(angle + M_PI) * moveDistance; + float newY = bot->GetPositionY() + sin(angle + M_PI) * moveDistance; + + // Only move if we have line of sight + if (bot->IsWithinLOS(newX, newY, bot->GetPositionZ())) { - dx /= len; - dy /= len; - float targetX = valanar->GetPositionX() + dx * 5.0f; - float targetY = valanar->GetPositionY() + dy * 5.0f; - float targetZ = valanar->GetPositionZ(); - bot->UpdateAllowedPositionZ(targetX, targetY, targetZ); - - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ); + return MoveAway(unit, moveDistance); } } } @@ -1706,10 +1822,15 @@ bool IccValithriaPortalAction::Execute(Event event) if (!portal) return false; - // Move to the portal if the bot is not at the interact distance - if (!portal->IsWithinDistInMap(bot, INTERACTION_DISTANCE)) + // Move exactly to portal position + float portalX = portal->GetPositionX(); + float portalY = portal->GetPositionY(); + float portalZ = portal->GetPositionZ(); + + // If not at exact portal position, move there + if (bot->GetDistance2d(portalX, portalY) > 0.1f) { - return MoveTo(portal, INTERACTION_DISTANCE); + return MoveTo(portal->GetMapId(), portalX, portalY, portalZ, false, false, false, true, MovementPriority::MOVEMENT_NORMAL); } // Remove shapeshift forms @@ -1732,6 +1853,12 @@ bool IccValithriaHealAction::Execute(Event event) if (!botAI->IsHeal(bot)) return false; + if (!bot->HasAura(70766)) //dream state + { + bot->SetSpeed(MOVE_RUN, 1.0f, true); + bot->SetSpeed(MOVE_WALK, 1.0f, true); + bot->SetSpeed(MOVE_FLIGHT, 1.0f, true); + } // Find Valithria if (Creature* valithria = bot->FindNearestCreature(36789, 100.0f)) { @@ -1757,6 +1884,15 @@ bool IccValithriaDreamCloudAction::Execute(Event event) if (!bot->HasAura(70766)) return false; + // Set speed to match players in dream state + if (bot->HasAura(70766)) + { + bot->SetSpeed(MOVE_RUN, 2.0f, true); + bot->SetSpeed(MOVE_WALK, 2.0f, true); + bot->SetSpeed(MOVE_FLIGHT, 2.0f, true); + } + + // Find nearest cloud of either type that we haven't collected Creature* dreamCloud = bot->FindNearestCreature(37985, 100.0f); Creature* nightmareCloud = bot->FindNearestCreature(38421, 100.0f); @@ -2102,7 +2238,7 @@ bool IccSindragosaFrostBeaconAction::Execute(Event event) // Only move if the change in position is significant if (std::abs(moveX) > MOVE_TOLERANCE || std::abs(moveY) > MOVE_TOLERANCE) { - return MoveTo(bot->GetMapId(), posX, posY, posZ, + return MoveTo(bot->GetMapId(), posX, posY, posZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); } } @@ -2576,7 +2712,7 @@ bool IccLichKingAddsAction::Execute(Event event) if (!defiles.empty()) { float baseRadius = 6.0f; - float safetyMargin = 1.0f; // Fixed 5-yard safety margin + float safetyMargin = 3.0f; // Fixed 3-yard safety margin // First, find if we need to move from any defile bool needToMove = false; @@ -2597,7 +2733,7 @@ bool IccLichKingAddsAction::Execute(Event event) if (growAura) { uint8 stacks = growAura->GetStackAmount(); - currentRadius = baseRadius + (stacks * 1.25f); + currentRadius = baseRadius + (stacks * 1.3f); } float dx = bot->GetPositionX() - defile->GetPositionX(); @@ -2741,14 +2877,14 @@ bool IccLichKingAddsAction::Execute(Event event) if (growAura) { uint8 stacks = growAura->GetStackAmount(); - currentRadius = 6.0f + (stacks * 1.25f); + currentRadius = 6.0f + (stacks * 1.3f); } float dx = testX - defile->GetPositionX(); float dy = testY - defile->GetPositionY(); float distToDefile = sqrt(dx * dx + dy * dy); - if (distToDefile < (currentRadius + 1.0f)) + if (distToDefile < (currentRadius + 3.0f)) { isSafeFromDefiles = false; break; diff --git a/src/strategy/raids/icecrown/RaidIccActions.h b/src/strategy/raids/icecrown/RaidIccActions.h index ec0f5a22..388140d4 100644 --- a/src/strategy/raids/icecrown/RaidIccActions.h +++ b/src/strategy/raids/icecrown/RaidIccActions.h @@ -28,12 +28,12 @@ const Position ICC_FESTERGUT_MELEE_SPORE = Position(4269.1772f, 3144.7673f, 360. const Position ICC_ROTFACE_TANK_POSITION = Position(4447.061f, 3150.9758f, 360.38568f); const Position ICC_ROTFACE_BIG_OOZE_POSITION = Position(4432.687f, 3142.3035f, 360.38623f); const Position ICC_ROTFACE_SAFE_POSITION = Position(4446.557f, 3065.6594f, 360.51843f); -const Position ICC_PUTRICIDE_TANK_OOZE_POSITION = Position(4362.709f, 3229.1448f, 389.4083f); -const Position ICC_PUTRICIDE_TANK_GAS_CLOUD_POSITION = Position(4397.0386f, 3221.385f, 389.3999f); -const Position ICC_PUTRICIDE_GAS1_POSITION = Position(4350.772f, 3249.9773f, 389.39508f); -const Position ICC_PUTRICIDE_GAS2_POSITION = Position(4390.002f, 3204.8855f, 389.39938f); -const Position ICC_PUTRICIDE_GAS3_POSITION = Position(4367.753f, 3177.5894f, 389.39575f); -const Position ICC_PUTRICIDE_GAS4_POSITION = Position(4321.8486f, 3206.464f, 389.3982f); +//const Position ICC_PUTRICIDE_TANK_OOZE_POSITION = Position(4362.709f, 3229.1448f, 389.4083f); +//const Position ICC_PUTRICIDE_TANK_GAS_CLOUD_POSITION = Position(4397.0386f, 3221.385f, 389.3999f); +//const Position ICC_PUTRICIDE_GAS1_POSITION = Position(4350.772f, 3249.9773f, 389.39508f); +//const Position ICC_PUTRICIDE_GAS2_POSITION = Position(4390.002f, 3204.8855f, 389.39938f); +//const Position ICC_PUTRICIDE_GAS3_POSITION = Position(4367.753f, 3177.5894f, 389.39575f); +//const Position ICC_PUTRICIDE_GAS4_POSITION = Position(4321.8486f, 3206.464f, 389.3982f); const Position ICC_BPC_OT_POSITION = Position(4649.2236f, 2796.0972f, 361.1815f); const Position ICC_BPC_MT_POSITION = Position(4648.5674f, 2744.847f, 361.18222f); const Position ICC_BQL_CENTER_POSITION = Position(4595.0f, 2769.0f, 400.0f); @@ -58,8 +58,6 @@ const Position ICC_LK_FROST3_POSITION = Position(503.40182f, -2067.3435f, 840.85 const Position ICC_LK_FROSTR1_POSITION = Position(481.168f, -2177.8723f, 840.857f); const Position ICC_LK_FROSTR2_POSITION = Position(562.20807f, -2100.2393f, 840.857f); const Position ICC_LK_FROSTR3_POSITION = Position(526.35297f, -2071.0317f, 840.857f); - - //Lord Marrogwar class IccLmTankPositionAction : public AttackAction @@ -236,14 +234,13 @@ class IccPutricideGasCloudAction : public AttackAction IccPutricideGasCloudAction(PlayerbotAI* botAI, std::string const name = "icc putricide gas cloud") : AttackAction(botAI, name) {} bool Execute(Event event) override; - private: - static uint8_t lastKnownPosition; // 0 = none, 1-4 = positions 1-4 }; class AvoidMalleableGooAction : public MovementAction { public: - AvoidMalleableGooAction(PlayerbotAI* ai) : MovementAction(ai, "avoid malleable goo") {} + AvoidMalleableGooAction(PlayerbotAI* botAI, std::string const name = "avoid malleable goo" ) + : MovementAction(botAI, name) {} bool Execute(Event event) override; }; diff --git a/src/strategy/raids/icecrown/RaidIccMultipliers.cpp b/src/strategy/raids/icecrown/RaidIccMultipliers.cpp index 0540d36b..339914ff 100644 --- a/src/strategy/raids/icecrown/RaidIccMultipliers.cpp +++ b/src/strategy/raids/icecrown/RaidIccMultipliers.cpp @@ -267,7 +267,7 @@ float IccAddsPutricideMultiplier::GetValue(Action* action) if (dynamic_cast(action) || dynamic_cast(action)) { - if (dynamic_cast(action)) + if (dynamic_cast(action) || dynamic_cast(action)) return 0.0f; } @@ -307,6 +307,22 @@ float IccBpcAssistMultiplier::GetValue(Action* action) if (!keleseth || !keleseth->IsAlive()) return 1.0f; + Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + if (aura) + { + if (aura->GetStackAmount() > 18 && botAI->IsTank(bot)) + { + if (dynamic_cast(action)) + return 0.0f; + } + + if (aura->GetStackAmount() > 12 && !botAI->IsTank(bot)) + { + if (dynamic_cast(action)) + return 0.0f; + } + } + // For assist tank during BPC fight if (botAI->IsAssistTank(bot)) { diff --git a/src/strategy/raids/icecrown/RaidIccStrategy.cpp b/src/strategy/raids/icecrown/RaidIccStrategy.cpp index 9c2828e5..f2b76c1d 100644 --- a/src/strategy/raids/icecrown/RaidIccStrategy.cpp +++ b/src/strategy/raids/icecrown/RaidIccStrategy.cpp @@ -76,19 +76,19 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) //PP triggers.push_back(new TriggerNode("icc putricide volatile ooze", - NextAction::array(0, new NextAction("icc putricide volatile ooze", ACTION_EMERGENCY + 4), nullptr))); + NextAction::array(0, new NextAction("icc putricide volatile ooze", ACTION_RAID + 4), nullptr))); triggers.push_back(new TriggerNode("icc putricide gas cloud", - NextAction::array(0, new NextAction("icc putricide gas cloud", ACTION_EMERGENCY + 5), nullptr))); + NextAction::array(0, new NextAction("icc putricide gas cloud", ACTION_RAID + 5), nullptr))); triggers.push_back(new TriggerNode("icc putricide growing ooze puddle", - NextAction::array(0, new NextAction("icc putricide growing ooze puddle", ACTION_EMERGENCY + 3), nullptr))); + NextAction::array(0, new NextAction("icc putricide growing ooze puddle", ACTION_RAID + 3), nullptr))); triggers.push_back(new TriggerNode("icc putricide main tank mutated plague", - NextAction::array(0, new NextAction("taunt spell", ACTION_EMERGENCY + 6), nullptr))); + NextAction::array(0, new NextAction("taunt spell", ACTION_RAID + 6), nullptr))); triggers.push_back(new TriggerNode("icc putricide malleable goo", - NextAction::array(0, new NextAction("avoid malleable goo", ACTION_EMERGENCY + 2), nullptr))); + NextAction::array(0, new NextAction("avoid malleable goo", ACTION_RAID + 2), nullptr))); //BPC triggers.push_back(new TriggerNode("icc bpc keleseth tank", diff --git a/src/strategy/raids/icecrown/RaidIccTriggers.cpp b/src/strategy/raids/icecrown/RaidIccTriggers.cpp index 0164eaa5..8d5997e9 100644 --- a/src/strategy/raids/icecrown/RaidIccTriggers.cpp +++ b/src/strategy/raids/icecrown/RaidIccTriggers.cpp @@ -274,7 +274,8 @@ bool IccFestergutSporeTrigger::IsActive() bool IccRotfaceTankPositionTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); - if (!boss || !(botAI->IsTank(bot) || botAI->IsMainTank(bot) || botAI->IsAssistTank(bot))) { return false; } + if (!boss || !(botAI->IsTank(bot) || botAI->IsMainTank(bot) || botAI->IsAssistTank(bot))) + return false; return true; } @@ -300,18 +301,37 @@ bool IccRotfaceMoveAwayFromExplosionTrigger::IsActive() bool IccPutricideGrowingOozePuddleTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); - bool botHasAura = botAI->HasAura("Gaseous Bloat", bot); + bool botHasAura = botAI->HasAura("Gaseous Bloat", bot) || botAI->HasAura("Volatile Ooze Adhesive", bot); if (!boss || botHasAura) return false; - return true; + // Check for nearby growing ooze puddles (37690) and slime puddles (70341) + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit) + continue; + + uint32 entry = unit->GetEntry(); + if (entry == 37690 || entry == 70341) // Growing Ooze Puddle or Slime Puddle + { + return true; + } + } + + return false; } bool IccPutricideVolatileOozeTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "volatile ooze"); - if (!boss) { return false; } + if (!boss) + return false; + + if (botAI->HasAura("Gaseous Bloat", bot)) + return false; return true; } @@ -319,7 +339,8 @@ bool IccPutricideVolatileOozeTrigger::IsActive() bool IccPutricideGasCloudTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "gas cloud"); - if (!boss) { return false; } + if (!boss) + return false; return true; } @@ -377,6 +398,11 @@ bool IccBpcKelesethTankTrigger::IsActive() if (!botAI->IsAssistTank(bot)) return false; + Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + if (aura) + if (aura->GetStackAmount() > 18) + return false; + // First priority is to check for nucleuses that need to be picked up GuidVector targets = AI_VALUE(GuidVector, "possible targets"); for (auto i = targets.begin(); i != targets.end(); ++i) @@ -401,6 +427,11 @@ bool IccBpcNucleusTrigger::IsActive() if (!botAI->IsAssistTank(bot)) return false; + Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + if (aura) + if (aura->GetStackAmount() > 18) + return false; + // Actively look for any nucleus that isn't targeting us GuidVector targets = AI_VALUE(GuidVector, "possible targets"); for (auto i = targets.begin(); i != targets.end(); ++i) @@ -435,6 +466,11 @@ bool IccBpcEmpoweredVortexTrigger::IsActive() if (!valanar || !valanar->IsAlive()) return false; + Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + if (aura) + if (aura->GetStackAmount() > 12) + return false; + // For ranged, spread whenever Valanar is empowered if (botAI->IsRanged(bot)) return valanar->HasAura(71596); // Invocation of Blood