ICC Sindragosa and LK Improve (#1216)

* ICC PP WIP

WIP

* added mutated plague for PP

* BPC added (kinetic and boss targeting need to be done by player)

OT collects dark nucles, bots spread on vortex and other stuff they do ok on their own.
Tested only on 10NM, should work on 25NM

* Tank pos. correction

* BQL, ranged spread, link, flame, bite, tanking tested 10NM

to do (better fire spread, hc tacti, melee spread when in air)

* LDW improved

improved shadow logic, ranged spread for easier shadow handling

* dbs update, fixed teleporting

Bots should only go and teleport to the mage that is actually below zero now

* DBS ranged fix

Ranged should spread more quickly and freak out less

* Festergut && DBS

fixed ranged spread (both)
fixed spore logic (fester)

* Rotface fix

Improved big ooze tanking (static pos, todo kiting)
ooze explosion spread mechanic fix
ooze pool fix
Player needs to mark rotface with skull icon, oterwise bots try to attack oozes

* BQL fixed for 25nm

todo: better melee logic in air phase, better melee flame spread

* VDW, Sister Svalna, Sindy update

Sister Svalna, bots can pickup spears and throw at svalna when she has shield up

VDW added healer strats to use portal and heal boss (atm druids are for raid healing only, so use druide + any other healer, ideally player should be healer)
todo (focus on supressers, add healer rotations, atm they use quickest spell they can)

Sindragosa
Added tank boss manipulation (boss orientation and position)
bots detect (buffet, unchained magic and chilled to the bone and act accordingly)
bots detect frost beacon move to safe spot and los frost bombs around them, while dpsing tombs (todo stop dps if only one tomb is left, if we have frost bombs around, not a big deal atm since in nm they dont one shot)
Last phase bots los behind tomb to loose buffet, tanks swap when they have hi buffet count.
Player should tell bots with skull rti if they should kill tomb or focus boss.
todo (dynamic tomb los in last phase so that healers can see tank but also hide behind tomb to break los from boss)

Removed some debug messages, improved LM spike action (nearest bots also try to help kill it)

Improved Lady Deathwshiper shade action (only targeted bots will run away instead of every bot that is near it)

dbs improved tank switch

I recommend to use 3 healers (just to be safe) and 2 paladin tanks (warr seems to struggle with agro) in 10 man
25 man 6-7 healers (just to be safe) Since most of the bosses are about survival and not dps

* LK Update (doable)

LK added

Improved tank switching for all bosses

Fixed PP gas cloud kiting
Malleable goo todo (dont know how to detect it since its not object or npc) just summon ranged bots to safe position to bypass

BPC  fixed OT sometimes not tanking kele
kinectic bombs todo (for now player should take care of them)

Sindragosa fixed rare case when she is in air phase but tombs went to last phase position

LK
Bots can handle necrotic
Bots can handle adds
Bots should focus valkyre that actually grabbed someone (if unlucky and player just use attack command and summon bots to you if they are far away from you) if they grab bots you can either summon to make them useless or let bots cc them and do it legit way.

Defile should be watched by player and once it was cast just summon bots to you
Vile spirits for some reason go to the ground and get nuked by bots aoe spells so there is not much to be done

**Player needs to be alive the whole LK fight since you will have to watch out for frost spehers (sometimes bots ignore them), summon bots when defile is up and summon ranged bots if they get stuck near shambling or raging spirits since their aoe will wipe you)

all in all LK  is doable both 10 and 25nm, player needs to have knowledge of lk fight and needs to know how to use multibot addon and make macros for eg summoning or commanding groups of bots or individual bots)

Dont forget frost/shadow/nature resist auras in whole ICC since it will help alot

I have done whole icc 10 and 25 with 2 pala tanks, 2/5 heals and rest dps,  if you use +1 or +2 heals it should be easier (since I was testing I did close to 0 dmg in fights same with heals)

* fixed changes made by mistake

fix

* Malleable fix (simple spread mechanic)

Malleable mechanic added (simple spread for now)
Gas cloud fixed (Bots sometimes got stuck between puddle and kite location)

* Defile Update

Bots detect and avoid defile (they struggle to find a way back to the boss around it tho, use summon to help them)

Melee bots should be able to stand behind/to the flank of shambling/spirits

* GS fixed bots not returning to their ship for A and H

Bots will return back to ship after killing mage

* PP gas cloud kiting improved

PP gas cloud kiting improved

* BPC targeting fixed

Bots will mark valid prince with skull RTI now

* BQL added melee spread in air pahse

BQL added melee spread

* VDW healing rotation improved

Healers will now use strong heals and hots

* Fixed Necrotic Plague

Fixed issue with Necrotic where it would get dispelled too soon, or would not get dispelled at all

* LK Update

Refined defile logic
Added 3 points for ranged and melee in winter phase (east, west, south when facing throne)
fixed frost spheres targeting (hunter will focus them)
Atm bots will reset z axis if they fall underground or if they get teleported by lk
Better positioning in 1st phase

* 10HC update until PP

LK defile improved for 10nm (bots sometimes stood 2 close to defile until it grew few times)

Improved rotface for HC

PP remade for 10HC.
Gas cloud is now properly kited
Fixed a rare case of server crash when there were ooze and gas cloud alive at same time.
Bots will move around puddles according to its size now.
Bots that get unboud plague will simply move away from raid and die thus loosing it from raid.
Volatile ooze improved stacking.
Fixed ranged sometimes glitching thru walls when spreading out from other members.

10HC PP is now doable but its hard without summoning (summoning break gascloud and ooze targets so its easier to do). You need to watch boss so you dont phase 2 soon otherwise you will get 2x ooze and cloud which is almoust always a wipe. If abo is not played near perfection bots will struggle with oozes and gas clouds if they are not slowed on time. Always save energy to slow gas cloud since it will wipe the group if it reach its target.
Bots will sometimes stand in puddle, just command them to move and they will figure out what to do.

todo (proper malleable handling)

* Up until Sindy 10HC

BPC added shadow prison handling, bots stop moving if more than 12 stack, tanks more than 18
Improved spreading logic

VDW
fixed issue where bots in portal wold move at half speed compared to real player

* fixed accidental change

* LK 10HC update

Added Shadow trap logic
(if they stand in it, not a big deal since bots wont get yeeted only players will)
When harvest soul, only player will be in another dimension (you must survive)

**Sindy and LK can be done, but I must dissapoint purist, at my skill level I could not achieve to do HC without using summon or other "cheat" bot functions.

Other bosses are all doable now in 10hc

* ICC fixes GS, PP; BQL, SINDY

Minor fixes, bite action improved

* ICC improve Sindy

Bots will now choose non beacon position based on difficulty, 10/25

* ICC fixed missing A/H buff

Fixed missing ICC buff for A and H

Buff will only be present when logged on and in ICC, once any bot or player leave ICC the buff is gone to prevent abuse.

This will make ICC easier now and with recent DPS update and movement improvement bots will now actully do decent dps and even greater healing.

Ally buff
https://www.wowhead.com/wotlk/spell=73828/strength-of-wrynn

Horde buff
https://www.wowhead.com/wotlk/spell=73822/hellscreams-warsong

* revert last change

revert buff

* ICC improve Rotface

Bots will now mark Rotface with skull icon, which will make them focus boss instead of oozes automatically

* ICC Festergut 10 Man fix

There was a rare case in 10 Man when 2 tanks would get spores which made them both stack at melee spot. Now the code will check if any of the spored player is main tank, if it is, it will stay at melee and other spores will go at ranged spot since off tank doesn't really need to stand near main tank all the time.

* ICC BPC major update, fix and improve

Fixed main tank sometimes not tanking both bosses (vala and talda)

Improved marking of current prince

Empowered vortex: bots will now spread out when it is being cast, instead of always spreading(ranged). This will make melee also spread better now since bots will calculate and move to optimal positions.

Added Kinetic bomb handling.
Hunters will take care of bombs, if no hunter is present then any ranged dps will take care of kinetic bombs.

* ICC BQL/VDW major update + minor fixes/improvements

LDW improved spreading for ranged, tanks will move boss in the middle of the room now when shield is depleted which will make bots bug less around pillars

GS Assist tank will not teleport to enemy ship and will keep tanking adds on our ship now

DBS ranged spread improved (they will calcualte spot for each bot and stick with it reducing movment to minimum), fixed bug where in 25 man ranged bots would go behind walls, making them unable to dps boss, improved rune of blood tanking

Festergut ranged spread improved (they will calcualte spot for each bot and stick with it reducing movment to minimum)

BQL Melee spread improved, bite logic improved, added swarming shadows logic (not perfect but at least it wont be all over the room anymore), Tanks will properly handle blood mirror now

VDW boss and raid healers will be automatically assinged depening by number of healers(if more than 3 healers in group, 2 will focus on raid and others will heal boss, otherwise one healer will heal raid). Druid will be assigned to raid healing if no druid present then some other random heal class. Added rotations for druid healers if they end up healing the boss. Raid healers will not use portals anymore. Healers will come to the ground now after using portals (they were stuck in air)

* ICC LK minor update

PP

Removed pre stacking for ranged when volatile ooze spawn (they will kill it much faster now and only stack if someone actually gets targeted by ooze)

LK

Hunters will use trnaq shot to remove enrage from shamblings

Improved valkyr cc

Minimized ping-ponging during winter phase

* ICC minor Sindy improve

Reduced position tolerances and forced movement for beacon and non beacon positions which will make bots move to spot that they actually need to be at instead of randomly running to sindragosa or beaconed players.

* ICC minor update

GS

Bots will mark mage with skull rti

Rotface

Fixed bots glitching thru walls and floors (added check if position is valid before moving)

PP

Bots will mark volatile ooze with skull rti now which will help them focus it and kill asap (usefull for heroic when both volatile ooze and gas cloud are present at the same time)

VDW

Added default group position in the middle if the room so that bots don't spread out too much which will force them to focus supressers more

Fixed Boss healers not keeping themself alive when low on HP

* ICC LK minor update

Commented out z axis bypass since it was fixed with recent core updates.

Bots no longer fall thru buggy platforms so its no longer needed which in return makes valkyrs works as they should.

* ICC LM & LDW Improved

LM

Removed Attack Action since it was buggy and replaced it with RTI. (Improved DPS)

Simplified trigger for spike action, since attack logic is handled by skull RTI now.

Bots (tanks) will mark spikes with skull RTI, which will make bots instantly switch to Spike targets. If no spike is present, the boss will be marked.

Added logic that will make non-tank bots move away from LM to a fixed position behind LM if they happened to be in front of LM (No more insta kills with cleave).
If LM is casting Bone Storm, bots will ignore the above logic and do max dps.

LDW

Removed Attack Action since it was buggy and replaced it with RTI. (Improved DPS)

Bots (tanks) will mark adds with skull RTI, which will make bots instantly switch to adds targets. If no spike is present, the boss will be marked, which will help with keeping allies alive when they get mind controlled by LDW.

Moved 2nd phase position deeper into the room and reduced boss hp trigger to 95%, which will keep tanks where they should be during 1st phase (sometimes the boss dropped below 98% and tanks would ping pong around the room).

Ranged bots will now spread closer to each other now and will ignore spreading if they are 2 far or 2 close to the boss so they can properly reposition.

These changes should also fix recent bugs with bots not casting/standing still on LM and LDW.

* ICC DBS Improve

Replaced attack action with RTI. (Improved DPS)

Ranged bots will move away from blood beast if targeted by it now.

* ICC Sindragosa and LK Improve

Sindragosa

Improved tomb los for frost bombs. Bots will now mark tomb with moon rti to avoid killing it too soon. (If they still attack the tomb, simply reset and after bombs are over, simply attack the tomb to kill or mark with a skull.)
Main Tank will reset mystic buffet stacks now to avoid tank swapping since it's really unreliable which will make sindy less frustrating to kill (other bots will need to do mechanics properly by hiding behind ice tombs).

LK

Necrotic plague action

Bots will now check before moving to the Shambling if they are in front of the Shambling to avoid getting one shoted by shockwave.
Improved multiplier, which will now properly handle dispelling of necrotic plague.

Winter phase action

Bots will properly move behind adds to avoid getting one shoted by shamblings or raging spirits.
If there is a hunter in the grouphunter will focus on spheres; if not, any ranged DPS will focus spheres.

Adds action

Bots will now be in proper positions during the 1st phase, and when the off tank is done collecting shamblings, it will move away from the group.
Bots (non-tanks) will move behind the shambling if they are in front of it to avoid getting one shoted in the 1st phase.
Restored action for checking if bots are at ground level, since they were still sometimes glitching through the floor (valkrys will be able to carry bots and drop them off the edge if not killed in time).
Improved defile calculations for 10 man and 25 man raids since defile was growing differently for 10 and 25, which would make bots in 25 man move very far away from somewhat small defile puddle.
Bots will now properly CC any valkyr that wasn't CC'd, and they won't freak out anymore if multiple valkyrs are present. (They can't tell which valkyr is holding bots/ players, so we should command an attack on valkyrs that are holding someone.)
Bots will now run away from Vile Spirits if they are targeted by them during the last phase.
This commit is contained in:
Noscopezz
2025-04-20 08:25:21 +02:00
committed by GitHub
parent bc2b83e759
commit eb3fa56829
3 changed files with 497 additions and 366 deletions

View File

@@ -2965,65 +2965,110 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event)
bool IccSindragosaFrostBombAction::Execute(Event event)
{
if (!bot || !bot->IsAlive() || bot->HasAura(70157)) // Skip if dead or in Ice Tomb
if (!bot || !bot->IsAlive() || bot->HasAura(70157)) // Skip if dead or in Ice Tomb
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
// Find frost bomb marker and tombs in one pass
Unit* marker = nullptr;
Unit* primaryTomb = nullptr;
float highestHealth = 0.0f;
int activeTombCount = 0;
// Find frost bomb marker and tombs
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
if (npcs.empty())
return false;
const uint32 tombEntries[] = {36980, 38320, 38321, 38322}; // tomb id's
Unit* marker = nullptr;
GuidVector tombGuids;
// Single pass to find marker and tombs
for (auto& npc : npcs)
// Process all NPCs
for (const ObjectGuid& guid : npcs)
{
Unit* unit = botAI->GetUnit(npc);
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
continue;
if (unit->HasAura(70022)) // Frost bomb visual
if (unit->HasAura(70022)) // Frost bomb visual
marker = unit;
// Check for any ice tomb variant
if (unit->GetEntry() == 36980 || unit->GetEntry() == 38320 ||
unit->GetEntry() == 38321 || unit->GetEntry() == 38322)
// Check if unit is a tomb
for (uint32 entry : tombEntries)
{
activeTombCount++;
if (unit->GetHealthPct() > highestHealth)
if (unit->GetEntry() == entry)
{
highestHealth = unit->GetHealthPct();
primaryTomb = unit;
tombGuids.push_back(guid);
break;
}
}
}
if (!marker || !primaryTomb)
// Exit if required elements aren't found
if (!marker || tombGuids.empty())
return false;
// Position handling
float angle = marker->GetAngle(primaryTomb);
float posX = primaryTomb->GetPositionX() + cos(angle) * 1.0f;
float posY = primaryTomb->GetPositionY() + sin(angle) * 1.0f;
float posZ = primaryTomb->GetPositionZ();
// Sort tomb GUIDs by health (highest first)
std::sort(tombGuids.begin(), tombGuids.end(),
[this](const ObjectGuid& a, const ObjectGuid& b)
{
Unit* unitA = botAI->GetUnit(a);
Unit* unitB = botAI->GetUnit(b);
if (!unitA || !unitB)
return false;
return unitA->GetHealthPct() > unitB->GetHealthPct();
});
// Check if we need to move
if (bot->GetDistance2d(posX, posY) > 2.0f)
// Get the tomb with highest HP for LOS
Unit* highestHPTomb = botAI->GetUnit(tombGuids[0]);
// Handle moon marking (much simpler now)
ObjectGuid currentMoon = group->GetTargetIcon(4);
// Check if current moon mark is still valid
bool moonMarkValid = false;
if (!currentMoon.IsEmpty())
{
return MoveTo(bot->GetMapId(), posX, posY, posZ,
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
for (const ObjectGuid& tombGuid : tombGuids)
{
if (tombGuid == currentMoon)
{
Unit* tomb = botAI->GetUnit(tombGuid);
if (tomb && tomb->IsAlive())
{
moonMarkValid = true;
break;
}
}
}
}
// Check if we have LOS to marker from our position
if (!marker->IsWithinLOS(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()))
return true; // Stay in position using tomb for LOS
// If no valid moon mark exists, mark the first tomb
if (!moonMarkValid && !tombGuids.empty())
{
Unit* tombToMark = botAI->GetUnit(tombGuids[0]);
if (tombToMark)
{
group->SetTargetIcon(4, bot->GetGUID(), tombToMark->GetGUID()); // Moon
// Reset bot's target after marking
bot->SetTarget(ObjectGuid::Empty);
}
}
// Use marked tomb or highest HP tomb for LOS check
Unit* losTarget = nullptr;
if (moonMarkValid)
{
losTarget = botAI->GetUnit(currentMoon);
}
else
{
losTarget = highestHPTomb;
}
// Position handling using highest HP tomb
float angle = marker->GetAngle(losTarget);
float posX = losTarget->GetPositionX() + cos(angle) * 1.0f;
float posY = losTarget->GetPositionY() + sin(angle) * 1.0f;
float posZ = losTarget->GetPositionZ();
if (bot->GetDistance2d(posX, posY) > 2.0f)
return MoveTo(bot->GetMapId(), posX, posY, posZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED);
return false;
}
@@ -3157,8 +3202,11 @@ bool IccLichKingNecroticPlagueAction::Execute(Event event)
// If we found a shambling and we're not close enough, move to it
if (closestHorror)
{
if (closestHorror->isInFront(bot))
return FleePosition(closestHorror->GetPosition(), 5.0f, 250U);
// If we're too far, run to it
if (minHorrorDist > 3.0f)
if (minHorrorDist > 4.0f)
{
// Check if there's a trap in our path
if (nearestTrap && minTrapDist < 9.0f)
@@ -3191,144 +3239,189 @@ bool IccLichKingNecroticPlagueAction::Execute(Event event)
bool IccLichKingWinterAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
if (!boss)
return false;
float currentDistance = bot->GetDistance2d(boss);
// Move behind target if bot is not tank, is close, and is in front
Unit* currentTarget = AI_VALUE(Unit*, "current target");
if (botAI->IsRanged(bot))
{
// Calculate distances to group positions
float dist1 = bot->GetDistance2d(ICC_LK_FROSTR1_POSITION.GetPositionX(), ICC_LK_FROSTR1_POSITION.GetPositionY());
float dist2 = bot->GetDistance2d(ICC_LK_FROSTR2_POSITION.GetPositionX(), ICC_LK_FROSTR2_POSITION.GetPositionY());
float dist3 = bot->GetDistance2d(ICC_LK_FROSTR3_POSITION.GetPositionX(), ICC_LK_FROSTR3_POSITION.GetPositionY());
if (!currentTarget)
{}
else if (currentTarget->isInFront(bot) && currentTarget->IsAlive() && !botAI->IsTank(bot) && bot->GetDistance2d(currentTarget) < 15.0f)
{
float orientation = currentTarget->GetOrientation() + M_PI + M_PI / 8;
float x = currentTarget->GetPositionX();
float y = currentTarget->GetPositionY();
float z = currentTarget->GetPositionZ();
float rx = x + cos(orientation) * 5.0f;
float ry = y + sin(orientation) * 5.0f;
return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT,
true, false);
}
const Position* targetPos = &ICC_LK_FROSTR1_POSITION;
if (dist2 < dist1 && dist2 < dist3)
if (botAI->IsRanged(bot))
{
// Calculate distances to group positions
float dist1 =
bot->GetDistance2d(ICC_LK_FROSTR1_POSITION.GetPositionX(), ICC_LK_FROSTR1_POSITION.GetPositionY());
float dist2 =
bot->GetDistance2d(ICC_LK_FROSTR2_POSITION.GetPositionX(), ICC_LK_FROSTR2_POSITION.GetPositionY());
float dist3 =
bot->GetDistance2d(ICC_LK_FROSTR3_POSITION.GetPositionX(), ICC_LK_FROSTR3_POSITION.GetPositionY());
const Position* targetPos = &ICC_LK_FROSTR1_POSITION;
if (dist2 < dist1 && dist2 < dist3)
targetPos = &ICC_LK_FROSTR2_POSITION;
else if (dist3 < dist1 && dist3 < dist2)
else if (dist3 < dist1 && dist3 < dist2)
targetPos = &ICC_LK_FROSTR3_POSITION;
float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY());
if (distToTarget > 7.0f)
{
float angle = bot->GetAngle(targetPos);
float posX = bot->GetPositionX() + cos(angle) * 5.0f;
float posY = bot->GetPositionY() + sin(angle) * 5.0f;
return MoveTo(bot->GetMapId(), posX, posY, 840.857f,
false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
}
}
else
float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY());
if (distToTarget > 3.0f)
{
// Calculate distances to tank positions
float dist1 = bot->GetDistance2d(ICC_LK_FROST1_POSITION.GetPositionX(), ICC_LK_FROST1_POSITION.GetPositionY());
float dist2 = bot->GetDistance2d(ICC_LK_FROST2_POSITION.GetPositionX(), ICC_LK_FROST2_POSITION.GetPositionY());
float dist3 = bot->GetDistance2d(ICC_LK_FROST3_POSITION.GetPositionX(), ICC_LK_FROST3_POSITION.GetPositionY());
const Position* targetPos = &ICC_LK_FROST1_POSITION;
if (dist2 < dist1 && dist2 < dist3)
targetPos = &ICC_LK_FROST2_POSITION;
else if (dist3 < dist1 && dist3 < dist2)
targetPos = &ICC_LK_FROST3_POSITION;
float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY());
if (distToTarget > 3.0f && !botAI->IsTank(bot))
{
float angle = bot->GetAngle(targetPos);
float posX = bot->GetPositionX() + cos(angle) * 5.0f;
float posY = bot->GetPositionY() + sin(angle) * 5.0f;
return MoveTo(bot->GetMapId(), posX, posY, 840.857f,
false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
}
if (distToTarget > 5.0f && botAI->IsTank(bot))
{
float angle = bot->GetAngle(targetPos);
float posX = bot->GetPositionX() + cos(angle) * 5.0f;
float posY = bot->GetPositionY() + sin(angle) * 5.0f;
return MoveTo(bot->GetMapId(), posX, posY, 840.857f,
false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
}
float angle = bot->GetAngle(targetPos);
float posX = bot->GetPositionX() + cos(angle) * 5.0f;
float posY = bot->GetPositionY() + sin(angle) * 5.0f;
return MoveTo(bot->GetMapId(), posX, posY, 840.857f, false, false, false, true,
MovementPriority::MOVEMENT_FORCED, true, false);
}
// Check for spheres if we're at a safe distance
if (bot->getClass() == CLASS_HUNTER)
}
else
{
Unit* closestSphere = nullptr;
float closestDist = 100.0f;
// Calculate distances to tank positions
float dist1 = bot->GetDistance2d(ICC_LK_FROST1_POSITION.GetPositionX(), ICC_LK_FROST1_POSITION.GetPositionY());
float dist2 = bot->GetDistance2d(ICC_LK_FROST2_POSITION.GetPositionX(), ICC_LK_FROST2_POSITION.GetPositionY());
float dist3 = bot->GetDistance2d(ICC_LK_FROST3_POSITION.GetPositionX(), ICC_LK_FROST3_POSITION.GetPositionY());
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (auto& npc : npcs)
const Position* targetPos = &ICC_LK_FROST1_POSITION;
if (dist2 < dist1 && dist2 < dist3)
targetPos = &ICC_LK_FROST2_POSITION;
else if (dist3 < dist1 && dist3 < dist2)
targetPos = &ICC_LK_FROST3_POSITION;
float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY());
if (distToTarget > 10.0f && !botAI->IsTank(bot))
{
Unit* unit = botAI->GetUnit(npc);
if (!unit || !unit->IsAlive())
continue;
float angle = bot->GetAngle(targetPos);
float posX = bot->GetPositionX() + cos(angle) * 5.0f;
float posY = bot->GetPositionY() + sin(angle) * 5.0f;
return MoveTo(bot->GetMapId(), posX, posY, 840.857f, false, false, false, false,
MovementPriority::MOVEMENT_FORCED, true, false);
}
uint32 entry = unit->GetEntry();
if (entry == 36633 || entry == 39305 || entry == 39306 || entry == 39307)
if (distToTarget > 2.0f && botAI->IsTank(bot))
{
float angle = bot->GetAngle(targetPos);
float posX = bot->GetPositionX() + cos(angle) * 5.0f;
float posY = bot->GetPositionY() + sin(angle) * 5.0f;
return MoveTo(bot->GetMapId(), posX, posY, 840.857f, false, false, false, true,
MovementPriority::MOVEMENT_FORCED, true, false);
}
}
// Check for spheres if we're at a safe distance
if (botAI->IsRangedDps(bot))
{
// First check if the group has a hunter
bool hasHunter = false;
Group* group = bot->GetGroup();
if (group)
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
float dist = bot->GetDistance(unit);
if (!closestSphere || dist < closestDist)
Player* member = itr->GetSource();
if (member && member->IsAlive() && member->IsInWorld() && member->getClass() == CLASS_HUNTER)
{
closestSphere = unit;
closestDist = dist;
hasHunter = true;
break;
}
}
}
if (closestSphere)
// If the bot is a hunter or the group has no hunter, allow attacking spheres
if (bot->getClass() == CLASS_HUNTER || !hasHunter)
{
return Attack(closestSphere);
Unit* closestSphere = nullptr;
float closestDist = 100.0f;
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (!unit || !unit->IsAlive())
continue;
uint32 entry = unit->GetEntry();
if (entry == 36633 || entry == 39305 || entry == 39306 || entry == 39307)
{
float dist = bot->GetDistance(unit);
if (!closestSphere || dist < closestDist)
{
closestSphere = unit;
closestDist = dist;
}
}
}
if (closestSphere)
{
bot->SetFacingToObject(closestSphere);
return Attack(closestSphere);
}
}
}
return false;
}
bool IccLichKingAddsAction::Execute(Event event)
{
if (bot->HasAura(68985) || !bot->IsAlive()) // Don't process actions if bot is picked up by Val'kyr or is dead
if (bot->HasAura(68985) || !bot->IsAlive()) // Don't process actions if bot is picked up by Val'kyr or is dead
return false;
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
Unit* spiritWarden = AI_VALUE2(Unit*, "find target", "spirit warden");
if (bot->HasAura(30440)) // Random aura tracking whether bot has fallen off edge / been thrown by Val'kyr
{
if(bot->GetPositionZ() > 779.0f)
return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f);
else
{
bot->KillPlayer(); // If bot has jumped past the kill Z (780), kill
return true;
}
}
bool hasPlague = botAI->HasAura("Necrotic Plague", bot);
if (!boss)
return false;
bool hasWinterAura = boss->HasAura(72259) || boss->HasAura(74273) || boss->HasAura(74274) || boss->HasAura(74275);
bool hasWinter2Aura = boss->HasAura(68981) || boss->HasAura(74270) || boss->HasAura(74271) || boss->HasAura(74272);
if (boss && boss->GetHealthPct() < 70 && boss->GetHealthPct() > 40 && !hasWinterAura && !hasWinter2Aura) // If boss is in p2, check if bot has been thrown off platform
if (bot->HasAura(30440)) // Random aura tracking whether bot has fallen off edge / been thrown by Val'kyr
{
float dx = bot->GetPositionX() - 503.0f;
float dy = bot->GetPositionY() - (-2124.0f);
float distance = sqrt(dx*dx + dy*dy); // Calculate distance from the center of the platform
if (distance > 52.0f && distance < 70.0f && bot->GetPositionZ() > 844) // If bot has fallen off edge, distance is over 52
{
bot->AddAura(30440, bot); // Apply random 30 sec aura to track that we've initiated a jump
return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f); // Start jumping to the abyss
}
if (bot->GetPositionZ() > 779.0f)
return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f);
else
{
bot->KillPlayer(); // If bot has jumped past the kill Z (780), kill
return true;
}
}
if (!boss)
{}
else
{
bool hasWinterAura = boss->HasAura(72259) || boss->HasAura(74273) || boss->HasAura(74274) || boss->HasAura(74275);
bool hasWinter2Aura = boss->HasAura(68981) || boss->HasAura(74270) || boss->HasAura(74271) || boss->HasAura(74272);
if (boss && boss->GetHealthPct() < 70 && boss->GetHealthPct() > 40 && !hasWinterAura &&
!hasWinter2Aura) // If boss is in p2, check if bot has been thrown off platform
{
float dx = bot->GetPositionX() - 503.0f;
float dy = bot->GetPositionY() - (-2124.0f);
float distance = sqrt(dx * dx + dy * dy); // Calculate distance from the center of the platform
if (distance > 52.0f && distance < 70.0f &&
bot->GetPositionZ() > 844) // If bot has fallen off edge, distance is over 52
{
bot->AddAura(30440, bot); // Apply random 30 sec aura to track that we've initiated a jump
return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(),
740.01f); // Start jumping to the abyss
}
}
}
/*
//temp solution for bots going underground due to buggy ice platfroms and adds that go underground
if (abs(bot->GetPositionZ() - 840.857f) > 1.0f)
return bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(),
bot->GetPositionY(), 840.857f, bot->GetOrientation());
*/
//temp soultion for bots when they get teleport far away into another dimension (they are unable to attack spirit warden) in heroic they will be in safe spot while player is surviving vile spirits
Difficulty diff = bot->GetRaidDifficulty();
@@ -3344,9 +3437,134 @@ bool IccLichKingAddsAction::Execute(Event event)
float posX = bot->GetPositionX() + cos(angle) * 10.0f;
float posY = bot->GetPositionY() + sin(angle) * 10.0f;
return MoveTo(bot->GetMapId(), posX, posY, boss->GetPositionZ(),
false, true, false, false, MovementPriority::MOVEMENT_COMBAT);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
// Find closest shambling horror
GuidVector npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs");
Unit* closestHorror = nullptr;
float minHorrorDistance = std::numeric_limits<float>::max();
for (auto& npc : npcs2)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && unit->IsAlive() &&
(unit->GetEntry() == 37698 || unit->GetEntry() == 39299 || unit->GetEntry() == 39300 ||
unit->GetEntry() == 39301)) // Shambling horror entries
{
float distance = bot->GetDistance(unit);
if (distance < minHorrorDistance)
{
minHorrorDistance = distance;
closestHorror = unit;
}
}
}
if (!closestHorror || hasPlague)
{}
else if (!hasPlague && closestHorror->isInFront(bot) && closestHorror->IsAlive() && !botAI->IsTank(bot) && bot->GetDistance2d(closestHorror) < 21.0f)
return FleePosition(closestHorror->GetPosition(), 2.0f, 250U);
// If bot is hunter and shambling is enraged, use Tranquilizing Shot
if (bot->getClass() == CLASS_HUNTER && closestHorror && botAI->HasAura("Enrage", closestHorror))
return botAI->CastSpell("Tranquilizing Shot", closestHorror);
if (!botAI->IsAssistTank(bot) && !boss->HealthBelowPct(71))
{
// Check for necrotic plague
bool hasPlague = botAI->HasAura("Necrotic Plague", bot);
// Only execute if we dont have the plague
if (!hasPlague)
{
float distanceToPosition = bot->GetDistance2d(ICC_LICH_KING_ADDS_POSITION.GetPositionX(),
ICC_LICH_KING_ADDS_POSITION.GetPositionY());
if (distanceToPosition < 15.0f) // Only move if we're closer than 15 yards to adds position
{
// Move directly away from adds position in 5yd increments
float angle = atan2(bot->GetPositionY() - ICC_LICH_KING_ADDS_POSITION.GetPositionY(),
bot->GetPositionX() - ICC_LICH_KING_ADDS_POSITION.GetPositionX());
float moveDistance = 5.0f; // Always move 5 yards away
float x = bot->GetPositionX() + cos(angle) * moveDistance; // Move from current position
float y = bot->GetPositionY() + sin(angle) * moveDistance;
float z = bot->GetPositionZ(); // Use bot's Z to prevent going underground
return MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
// Handle assist tanks - keep them at adds position
if (botAI->IsAssistTank(bot) && !boss->HealthBelowPct(70))
{
// Actively look for any shambling/spirit/ghoul that isn't targeting us
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
for (auto i = targets.begin(); i != targets.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (unit && unit->IsAlive() &&
(unit->GetEntry() == 37698 || unit->GetEntry() == 39299 || unit->GetEntry() == 39300 ||
unit->GetEntry() == 39301 || // Shambling entry
unit->GetEntry() == 36701 || unit->GetEntry() == 39302 || unit->GetEntry() == 39303 ||
unit->GetEntry() == 39304 || // Spirits entry
unit->GetEntry() == 37695 || unit->GetEntry() == 39309 || unit->GetEntry() == 39310 ||
unit->GetEntry() == 39311)) // Drudge Ghouls entry
{
if (!unit->GetVictim() || unit->GetVictim() != bot)
{
bot->SetFacingToObject(unit);
return Attack(unit); // Pick up any shambling that isn't targeting us
}
}
}
// Return to adds position if we're too far
if (bot->GetExactDist2d(ICC_LICH_KING_ADDS_POSITION) > 5.0f && !boss->HealthBelowPct(70))
{
return MoveTo(bot->GetMapId(), ICC_LICH_KING_ADDS_POSITION.GetPositionX(),
ICC_LICH_KING_ADDS_POSITION.GetPositionY(), ICC_LICH_KING_ADDS_POSITION.GetPositionZ(), false,
false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
}
return false; // Stay in position and keep facing current target
}
if (botAI->IsMainTank(bot) && !boss->HealthBelowPct(71))
{
float currentDist = bot->GetDistance(ICC_LICH_KING_ADDS_POSITION);
if (currentDist < 20.0f || currentDist > 30.0f) // 15 yards ±3 yards tolerance
{
float angle = ICC_LICH_KING_ADDS_POSITION.GetAngle(bot);
float targetDist = 21.0f;
float moveDistance = currentDist < targetDist ? 3.0f : -3.0f; // Move away or towards in 5yd increments
float x = bot->GetPositionX() + cos(angle) * moveDistance;
float y = bot->GetPositionY() + sin(angle) * moveDistance;
float z = ICC_LICH_KING_ADDS_POSITION.GetPositionZ();
return MoveTo(bot->GetMapId(), x, y, z, false, false, false, true, MovementPriority::MOVEMENT_FORCED, true,
false);
}
}
if (botAI->IsRanged(bot) && !boss->HealthBelowPct(71))
{
float currentDist = bot->GetDistance(ICC_LICH_KING_ADDS_POSITION);
if (currentDist < 21.0f) // 20 yards ±3 yards tolerance
{
float angle = ICC_LICH_KING_ADDS_POSITION.GetAngle(bot);
float targetDist = 26.0f;
float moveDistance = currentDist < targetDist ? 5.0f : -5.0f; // Move away or towards in 5yd increments
float x = bot->GetPositionX() + cos(angle) * moveDistance;
float y = bot->GetPositionY() + sin(angle) * moveDistance;
float z = ICC_LICH_KING_ADDS_POSITION.GetPositionZ();
return MoveTo(bot->GetMapId(), x, y, z, false, false, false, true, MovementPriority::MOVEMENT_FORCED, true,
false);
}
}
std::vector<Unit*> defiles;
Unit* closestDefile = nullptr;
@@ -3390,13 +3608,18 @@ bool IccLichKingAddsAction::Execute(Event event)
{
growAura = defile->GetAura(74162);
if (!growAura)
growAura = defile->GetAura(74163); //25hc mabye 74164
growAura = defile->GetAura(74163);
if (!growAura)
growAura = defile->GetAura(74164); // 25hc mabye 74164
}
if (growAura)
{
uint8 stacks = growAura->GetStackAmount();
currentRadius = baseRadius + (stacks * 1.3f);
if (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_10MAN_NORMAL)
currentRadius = baseRadius + (stacks * 1.4f);
else
currentRadius = baseRadius + (stacks * 0.95f);
}
float dx = bot->GetPositionX() - defile->GetPositionX();
@@ -3538,13 +3761,18 @@ bool IccLichKingAddsAction::Execute(Event event)
{
growAura = defile->GetAura(74162);
if (!growAura)
growAura = defile->GetAura(74163); //25hc mabye 74164
growAura = defile->GetAura(74163);
if (!growAura)
growAura = defile->GetAura(74164);
}
if (growAura)
{
uint8 stacks = growAura->GetStackAmount();
currentRadius = 6.0f + (stacks * 1.3f);
if (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_10MAN_NORMAL)
currentRadius = 6.0f + (stacks * 1.4f);
else
currentRadius = 6.0f + (stacks * 0.95f);
}
float dx = testX - defile->GetPositionX();
@@ -3609,226 +3837,108 @@ bool IccLichKingAddsAction::Execute(Event event)
}
}
// Check for Val'kyr Shadowguard targeting real players
// Check for Val'kyr Shadowguards grabbing units
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
Unit* highestPriorityValkyr = nullptr;
float closestValkyrDistance = std::numeric_limits<float>::max();
// First pass: Find all Val'kyrs and identify the highest priority target
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && unit->IsAlive() &&
(unit->GetEntry() == 36609 || unit->GetEntry() == 39120 || unit->GetEntry() == 39121 ||
unit->GetEntry() == 39122)) // Val'kyr Shadowguard entries
if (!unit || !unit->IsAlive())
continue;
// Check if this is a Val'kyr Shadowguard
if (unit->GetEntry() == 36609 || unit->GetEntry() == 39120 || unit->GetEntry() == 39121 ||
unit->GetEntry() == 39122)
{
GuidVector npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs");
Unit* closestValkyr = nullptr;
float minValkyrDistance = std::numeric_limits<float>::max();
float currentDistance = bot->GetDistance(unit);
// First find the closest Val'kyr to this bot
for (auto& npc2 : npcs2)
// Check if this Val'kyr is grabbing someone (aura 68985)
bool isGrabbing = unit->HasAura(68985);
// Prioritize Val'kyrs that are currently grabbing someone, then the closest one
if (!highestPriorityValkyr ||
(isGrabbing && (!highestPriorityValkyr->HasAura(68985) || currentDistance < closestValkyrDistance)) ||
(!highestPriorityValkyr->HasAura(68985) && currentDistance < closestValkyrDistance))
{
Unit* unit2 = botAI->GetUnit(npc2);
if (unit2 && unit2->IsAlive() &&
(unit2->GetEntry() == 36609 || unit2->GetEntry() == 39120 || unit2->GetEntry() == 39121 ||
unit2->GetEntry() == 39122)) // Val'kyr Shadowguard entries
{
float distance = bot->GetDistance(unit2);
if (distance < minValkyrDistance)
{
minValkyrDistance = distance;
closestValkyr = unit2;
}
}
}
if (closestValkyr)
{
// Check if this Val'kyr is grabbing any player
bool isGrabbingPlayer = false;
Map::PlayerList const& playerList = closestValkyr->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
{
Player* player = itr->GetSource();
if (!player || !player->IsAlive())
continue;
// Check if player is being lifted by Val'kyr (close horizontally and elevated)
float horizontalDist =
std::sqrt(std::pow(closestValkyr->GetPositionX() - player->GetPositionX(), 2) +
std::pow(closestValkyr->GetPositionY() - player->GetPositionY(), 2));
float verticalDist = player->GetPositionZ() - closestValkyr->GetPositionZ();
if (horizontalDist <= 1.0f && verticalDist > 0.5f) // Player is close horizontally and lifted up
{
isGrabbingPlayer = true;
break;
}
}
if (isGrabbingPlayer)
{
// Try to CC the Val'kyr based on class priority - only stuns and slows
if (bot->getClass() == CLASS_MAGE && !botAI->HasAura("Frost Nova", closestValkyr))
{
return botAI->CastSpell("Frost Nova", closestValkyr);
}
else if (bot->getClass() == CLASS_DRUID && !botAI->HasAura("Entangling Roots", closestValkyr))
{
return botAI->CastSpell("Entangling Roots", closestValkyr);
}
else if (bot->getClass() == CLASS_PALADIN && !botAI->HasAura("Hammer of Justice", closestValkyr))
{
return botAI->CastSpell("Hammer of Justice", closestValkyr);
}
else if (bot->getClass() == CLASS_WARRIOR && !botAI->HasAura("Hamstring", closestValkyr))
{
return botAI->CastSpell("Hamstring", closestValkyr);
}
else if (bot->getClass() == CLASS_HUNTER && !botAI->HasAura("Concussive Shot", closestValkyr))
{
return botAI->CastSpell("Concussive Shot", closestValkyr);
}
else if (bot->getClass() == CLASS_ROGUE && !botAI->HasAura("Kidney Shot", closestValkyr))
{
return botAI->CastSpell("Kidney Shot", closestValkyr);
}
else if (bot->getClass() == CLASS_SHAMAN && !botAI->HasAura("Frost Shock", closestValkyr))
{
return botAI->CastSpell("Frost Shock", closestValkyr);
}
else if (bot->getClass() == CLASS_DEATH_KNIGHT && !botAI->HasAura("Chains of Ice", closestValkyr))
{
return botAI->CastSpell("Chains of Ice", closestValkyr);
}
}
// If no CC available or all failed, attack the Val'kyr
return Attack(closestValkyr);
highestPriorityValkyr = unit;
closestValkyrDistance = currentDistance;
}
}
}
// Find closest shambling horror
GuidVector npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs");
Unit* closestHorror = nullptr;
float minHorrorDistance = std::numeric_limits<float>::max();
// Second pass: Handle the highest priority Val'kyr
if (highestPriorityValkyr && !botAI->IsMainTank(bot) && !botAI->IsHeal(bot))
{
// Try to CC the Val'kyr based on class priority
bool ccAttempted = false;
for (auto& npc : npcs2)
if (bot->getClass() == CLASS_MAGE && !botAI->HasAura("Frost Nova", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Frost Nova", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_DRUID && !botAI->HasAura("Entangling Roots", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Entangling Roots", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_PALADIN && !botAI->HasAura("Hammer of Justice", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Hammer of Justice", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_WARRIOR && !botAI->HasAura("Hamstring", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Hamstring", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_HUNTER && !botAI->HasAura("Concussive Shot", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Concussive Shot", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_ROGUE && !botAI->HasAura("Kidney Shot", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Kidney Shot", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_SHAMAN && !botAI->HasAura("Frost Shock", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Frost Shock", highestPriorityValkyr);
}
else if (bot->getClass() == CLASS_DEATH_KNIGHT && !botAI->HasAura("Chains of Ice", highestPriorityValkyr))
{
ccAttempted = botAI->CastSpell("Chains of Ice", highestPriorityValkyr);
}
// If CC was attempted (success or fail) or if no CC available, attack the Val'kyr
if (ccAttempted)
{
bot->SetFacingToObject(highestPriorityValkyr);
return Attack(highestPriorityValkyr);
}
return false;
}
const float radiusvile = 12.0f;
// Get the nearest hostile NPCs
GuidVector npcs3 = AI_VALUE(GuidVector, "nearest hostile npcs");
for (auto& npc : npcs3)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && unit->IsAlive() &&
(unit->GetEntry() == 37698 || unit->GetEntry() == 39299 || unit->GetEntry() == 39300 || unit->GetEntry() == 39301)) // Shambling horror entries
if (unit && (unit->GetEntry() == 37799 || unit->GetEntry() == 39284 || unit->GetEntry() == 39285 || unit->GetEntry() == 39286)) // Vile spirits ID
{
float distance = bot->GetDistance(unit);
if (distance < minHorrorDistance)
// Only run away if the vile spirit is targeting us
if (unit->GetVictim() == bot && !botAI->IsTank(bot))
{
minHorrorDistance = distance;
closestHorror = unit;
}
}
}
float currentDistance = bot->GetDistance2d(unit);
// If bot is hunter and shambling is enraged, use Tranquilizing Shot
if (bot->getClass() == CLASS_HUNTER && closestHorror && botAI->HasAura("Enrage", closestHorror))
return botAI->CastSpell("Tranquilizing Shot", closestHorror);
if (!botAI->IsAssistTank(bot) && !boss->HealthBelowPct(71))
{
// Check for necrotic plague
bool hasPlague = botAI->HasAura("Necrotic Plague", bot);
// Only execute if we dont have the plague
if (!hasPlague)
{
float distanceToPosition = bot->GetDistance2d(ICC_LICH_KING_ADDS_POSITION.GetPositionX(), ICC_LICH_KING_ADDS_POSITION.GetPositionY());
if (distanceToPosition < 15.0f) // Only move if we're closer than 15 yards to adds position
{
// Move directly away from adds position in 5yd increments
float angle = atan2(bot->GetPositionY() - ICC_LICH_KING_ADDS_POSITION.GetPositionY(),
bot->GetPositionX() - ICC_LICH_KING_ADDS_POSITION.GetPositionX());
float moveDistance = 5.0f; // Always move 5 yards away
float x = bot->GetPositionX() + cos(angle) * moveDistance; // Move from current position
float y = bot->GetPositionY() + sin(angle) * moveDistance;
float z = bot->GetPositionZ(); // Use bot's Z to prevent going underground
return MoveTo(bot->GetMapId(), x, y, z,
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
// Handle assist tanks - keep them at adds position
if (botAI->IsAssistTank(bot))
{
// Actively look for any shambling/spirit/ghoul that isn't targeting us
GuidVector targets = AI_VALUE(GuidVector, "possible targets");
for (auto i = targets.begin(); i != targets.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (unit && unit->IsAlive() && (unit->GetEntry() == 37698 || unit->GetEntry() == 39299 ||
unit->GetEntry() == 39300 || unit->GetEntry() == 39301 || // Shambling entry
unit->GetEntry() == 36701 || unit->GetEntry() == 39302 ||
unit->GetEntry() == 39303 || unit->GetEntry() == 39304 || // Spirits entry
unit->GetEntry() == 37695 || unit->GetEntry() == 39309 ||
unit->GetEntry() == 39310 || unit->GetEntry() == 39311)) // Drudge Ghouls entry
{
if (!unit->GetVictim() || unit->GetVictim() != bot)
// Move away from the Vengeful Shade if the bot is too close
if (currentDistance < radiusvile)
{
bot->SetFacingToObject(unit);
return Attack(unit); // Pick up any shambling that isn't targeting us
return MoveAway(unit, radiusvile - currentDistance);
}
}
}
// If we have a current target, make sure we're facing it
if (Unit* currentTarget = bot->GetVictim())
{
bot->SetFacingToObject(currentTarget);
}
// Return to adds position if we're too far
if (bot->GetExactDist2d(ICC_LICH_KING_ADDS_POSITION) > 3.0f && !boss->HealthBelowPct(71))
{
return MoveTo(bot->GetMapId(), ICC_LICH_KING_ADDS_POSITION.GetPositionX(),
ICC_LICH_KING_ADDS_POSITION.GetPositionY(),
ICC_LICH_KING_ADDS_POSITION.GetPositionZ(),
false, true, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false; // Stay in position and keep facing current target
}
if (botAI->IsMainTank(bot) && !boss->HealthBelowPct(71))
{
float currentDist = bot->GetDistance(ICC_LICH_KING_ADDS_POSITION);
if (currentDist < 9.0f || currentDist > 22.0f) // 15 yards ±3 yards tolerance
{
float angle = ICC_LICH_KING_ADDS_POSITION.GetAngle(bot);
float targetDist = 16.0f;
float moveDistance = currentDist < targetDist ? 5.0f : -5.0f; // Move away or towards in 5yd increments
float x = bot->GetPositionX() + cos(angle) * moveDistance;
float y = bot->GetPositionY() + sin(angle) * moveDistance;
float z = ICC_LICH_KING_ADDS_POSITION.GetPositionZ();
return MoveTo(bot->GetMapId(), x, y, z,
false, true, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
if (botAI->IsRanged(bot) && !boss->HealthBelowPct(71))
{
float currentDist = bot->GetDistance(ICC_LICH_KING_ADDS_POSITION);
if (currentDist < 21.0f) // 20 yards ±3 yards tolerance
{
float angle = ICC_LICH_KING_ADDS_POSITION.GetAngle(bot);
float targetDist = 26.0f;
float moveDistance = currentDist < targetDist ? 5.0f : -5.0f; // Move away or towards in 5yd increments
float x = bot->GetPositionX() + cos(angle) * moveDistance;
float y = bot->GetPositionY() + sin(angle) * moveDistance;
float z = ICC_LICH_KING_ADDS_POSITION.GetPositionZ();
return MoveTo(bot->GetMapId(), x, y, z,
false, true, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;

View File

@@ -28,7 +28,7 @@ uint32 g_lastPlagueTime = 0;
bool g_plagueAllowedToCure = false;
std::map<ObjectGuid, uint32> g_plagueTimes;
std::map<ObjectGuid, bool> g_allowCure;
std::mutex g_plagueMutex; // Add mutex for thread safety
std::mutex g_plagueMutex; // Lock before accessing shared variables
}
@@ -577,22 +577,26 @@ float IccSindragosaFrostBombMultiplier::GetValue(Action* action)
else if (dynamic_cast<CombatFormationMoveAction*>(action) ||
dynamic_cast<IccSindragosaTankPositionAction*>(action)
|| dynamic_cast<IccSindragosaBlisteringColdAction*>(action)
|| dynamic_cast<FollowAction*>(action) || dynamic_cast<AttackAction*>(action))
|| dynamic_cast<FollowAction*>(action))
return 0.0f;
return 1.0f;
}
float IccLichKingNecroticPlagueMultiplier::GetValue(Action* action)
{
// Allow plague movement action to proceed
if (dynamic_cast<IccLichKingNecroticPlagueAction*>(action))
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
if (!boss)
return 1.0f;
// Block combat formation and cure actions by default
else if (dynamic_cast<CombatFormationMoveAction*>(action))
if (!botAI->IsHeal(bot) && (dynamic_cast<CurePartyMemberAction*>(action) || dynamic_cast<CastCleanseDiseaseAction*>(action) ||
dynamic_cast<CastCleanseDiseaseOnPartyAction*>(action) ||
dynamic_cast<CastCleanseSpiritCurseOnPartyAction*>(action) || dynamic_cast<CastCleanseSpiritAction*>(action)))
return 0.0f;
// Handle cure actions
if (dynamic_cast<CurePartyMemberAction*>(action))
if (dynamic_cast<CurePartyMemberAction*>(action) || dynamic_cast<CastCleanseDiseaseAction*>(action) ||
dynamic_cast<CastCleanseDiseaseOnPartyAction*>(action) ||
dynamic_cast<CastCleanseSpiritCurseOnPartyAction*>(action) || dynamic_cast<CastCleanseSpiritAction*>(action))
{
Group* group = bot->GetGroup();
if (!group)
@@ -600,6 +604,7 @@ float IccLichKingNecroticPlagueMultiplier::GetValue(Action* action)
// Check if any bot in the group has plague
bool anyBotHasPlague = false;
ObjectGuid plaguedPlayerGuid; // Track who has plague
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
if (Player* member = ref->GetSource())
@@ -607,6 +612,7 @@ float IccLichKingNecroticPlagueMultiplier::GetValue(Action* action)
if (botAI->HasAura("Necrotic Plague", member))
{
anyBotHasPlague = true;
plaguedPlayerGuid = member->GetGUID(); // Changed from GetObjectGuid()
break;
}
}
@@ -617,35 +623,44 @@ float IccLichKingNecroticPlagueMultiplier::GetValue(Action* action)
// Reset state if no one has plague
if (!anyBotHasPlague)
{
g_lastPlagueTime = 0;
g_plagueAllowedToCure = false;
std::lock_guard<std::mutex> lock(g_plagueMutex); // Properly initialized
g_plagueTimes.clear();
g_allowCure.clear();
return 1.0f;
}
// Start timer if this is a new plague
if (g_lastPlagueTime == 0)
{
g_lastPlagueTime = currentTime;
g_plagueAllowedToCure = false;
return 0.0f;
}
{ // New scope for lock_guard
std::lock_guard<std::mutex> lock(g_plagueMutex); // Properly initialized
// Once we allow cure, keep allowing it until plague is gone
if (g_plagueAllowedToCure)
{
return 1.0f;
}
// Start timer if this is a new plague
if (g_plagueTimes.find(plaguedPlayerGuid) == g_plagueTimes.end())
{
g_plagueTimes[plaguedPlayerGuid] = currentTime;
g_allowCure[plaguedPlayerGuid] = false;
return 0.0f;
}
// Check if enough time has passed (3,5 seconds)
if (currentTime - g_lastPlagueTime >= 3500)
{
g_plagueAllowedToCure = true;
return 1.0f;
}
// Once we allow cure, keep allowing it until plague is gone
if (g_allowCure[plaguedPlayerGuid])
{
return 1.0f;
}
// Check if enough time has passed (3 seconds)
if (currentTime - g_plagueTimes[plaguedPlayerGuid] >= 3000)
{
g_allowCure[plaguedPlayerGuid] = true;
return 1.0f;
}
} // lock_guard is automatically released here
return 0.0f;
}
// Block combat formation actions by default
if (dynamic_cast<CombatFormationMoveAction*>(action))
return 0.0f;
return 1.0f;
}
@@ -670,8 +685,6 @@ float IccLichKingAddsMultiplier::GetValue(Action* action)
return 1.0f;
}
//melee reach, spell reach, ranged reach
if (botAI->IsRanged(bot))
{
// Check for defile presence

View File

@@ -851,6 +851,10 @@ bool IccSindragosaMainTankMysticBuffetTrigger::IsActive()
if (!boss)
return false;
Aura* aura = botAI->GetAura("mystic buffet", bot, false, false);
if (botAI->IsTank(bot) && aura) //main tank will delete mystic buffet until I find a better way to swap tanks, atm it is not great since while swapping they will wipe group 7/10 times.
bot->RemoveAura(aura->GetId());
if (botAI->IsTank(bot) && boss->GetVictim() == bot)
return false;
@@ -926,6 +930,10 @@ bool IccSindragosaTankSwapPositionTrigger::IsActive()
bool IccSindragosaFrostBombTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa");
if (!boss)
return false;
if (!bot->IsAlive() || bot->HasAura(70157)) // Skip if dead or in Ice Tomb
return false;