LK HC update (#876)

* 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
This commit is contained in:
Noscopezz
2025-01-18 18:04:26 +01:00
committed by GitHub
parent 89590860fb
commit e27c28b125
8 changed files with 342 additions and 215 deletions

View File

@@ -52,6 +52,7 @@ public:
creators["icc sindragosa mystic buffet"] = &RaidIccActionContext::icc_sindragosa_mystic_buffet;
creators["icc sindragosa frost bomb"] = &RaidIccActionContext::icc_sindragosa_frost_bomb;
creators["icc sindragosa tank swap position"] = &RaidIccActionContext::icc_sindragosa_tank_swap_position;
creators["icc lich king shadow trap"] = &RaidIccActionContext::icc_lich_king_shadow_trap;
creators["icc lich king necrotic plague"] = &RaidIccActionContext::icc_lich_king_necrotic_plague;
creators["icc lich king winter"] = &RaidIccActionContext::icc_lich_king_winter;
creators["icc lich king adds"] = &RaidIccActionContext::icc_lich_king_adds;
@@ -100,6 +101,7 @@ private:
static Action* icc_sindragosa_mystic_buffet(PlayerbotAI* ai) { return new IccSindragosaMysticBuffetAction(ai); }
static Action* icc_sindragosa_frost_bomb(PlayerbotAI* ai) { return new IccSindragosaFrostBombAction(ai); }
static Action* icc_sindragosa_tank_swap_position(PlayerbotAI* ai) { return new IccSindragosaTankSwapPositionAction(ai); }
static Action* icc_lich_king_shadow_trap(PlayerbotAI* ai) { return new IccLichKingShadowTrapAction(ai); }
static Action* icc_lich_king_necrotic_plague(PlayerbotAI* ai) { return new IccLichKingNecroticPlagueAction(ai); }
static Action* icc_lich_king_winter(PlayerbotAI* ai) { return new IccLichKingWinterAction(ai); }
static Action* icc_lich_king_adds(PlayerbotAI* ai) { return new IccLichKingAddsAction(ai); }

View File

@@ -1956,7 +1956,7 @@ bool IccSindragosaTankPositionAction::Execute(Event event)
float moveY = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionY() + (dirY / distBossToCenter) * 10.0f;
return MoveTo(bot->GetMapId(), moveX, moveY, boss->GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_NORMAL);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
// Stage 2: Get to tank position when boss is centered
@@ -1965,7 +1965,7 @@ bool IccSindragosaTankPositionAction::Execute(Event event)
return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_TANK_POSITION.GetPositionX(),
ICC_SINDRAGOSA_TANK_POSITION.GetPositionY(),
ICC_SINDRAGOSA_TANK_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_NORMAL);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
// Stage 3: Adjust orientation when in position
@@ -1992,7 +1992,7 @@ bool IccSindragosaTankPositionAction::Execute(Event event)
}
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_NORMAL);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
@@ -2023,9 +2023,9 @@ bool IccSindragosaTankPositionAction::Execute(Event event)
float stepSize = std::min(MAX_STEP, distToTarget);
float moveX = bot->GetPositionX() + dirX * stepSize;
float moveY = bot->GetPositionY() + dirY * stepSize;
return MoveTo(bot->GetMapId(), moveX, moveY, ICC_SINDRAGOSA_RANGED_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_NORMAL);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
@@ -2054,7 +2054,7 @@ bool IccSindragosaTankPositionAction::Execute(Event event)
float moveY = bot->GetPositionY() + dirY * stepSize;
return MoveTo(bot->GetMapId(), moveX, moveY, ICC_SINDRAGOSA_MELEE_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_NORMAL);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
@@ -2069,7 +2069,7 @@ bool IccSindragosaTankSwapPositionAction::Execute(Event event)
return false;
// Only for assist tank
if (!botAI->IsAssistTankOfIndex(bot, 0))
if (!botAI->IsAssistTank(bot))
return false;
float distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION);
@@ -2080,7 +2080,7 @@ bool IccSindragosaTankSwapPositionAction::Execute(Event event)
return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_TANK_POSITION.GetPositionX(),
ICC_SINDRAGOSA_TANK_POSITION.GetPositionY(),
ICC_SINDRAGOSA_TANK_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
@@ -2106,7 +2106,7 @@ bool IccSindragosaFrostBeaconAction::Execute(Event event)
ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionX(),
ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionY(),
ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
false, false, false, false, MovementPriority::MOVEMENT_FORCED);
}
return false;
}
@@ -2163,7 +2163,7 @@ bool IccSindragosaFrostBeaconAction::Execute(Event event)
return MoveTo(bot->GetMapId(), tombPosition->GetPositionX(),
tombPosition->GetPositionY(),
tombPosition->GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
false, false, false, false, MovementPriority::MOVEMENT_FORCED);
}
return false;
}
@@ -2203,7 +2203,7 @@ bool IccSindragosaFrostBeaconAction::Execute(Event event)
return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_FBOMB_POSITION.GetPositionX(),
ICC_SINDRAGOSA_FBOMB_POSITION.GetPositionY(),
ICC_SINDRAGOSA_FBOMB_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -2239,7 +2239,7 @@ bool IccSindragosaFrostBeaconAction::Execute(Event event)
if (std::abs(moveX) > MOVE_TOLERANCE || std::abs(moveY) > MOVE_TOLERANCE)
{
return MoveTo(bot->GetMapId(), posX, posY, posZ,
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
@@ -2255,25 +2255,46 @@ bool IccSindragosaBlisteringColdAction::Execute(Event event)
return false;
// Only non-tanks should move out
if (botAI->IsTank(bot) || botAI->IsMainTank(bot) || botAI->IsAssistTank(bot))
if (botAI->IsMainTank(bot))
return false;
float dist = bot->GetDistance(boss);
// Only move if we're too close to the boss (< 26 yards)
if (dist < 26.0f)
{
float const MAX_STEP = 5.0f;
float distToSafeSpot = bot->GetDistance2d(ICC_SINDRAGOSA_BLISTERING_COLD_POSITION.GetPositionX(),
ICC_SINDRAGOSA_BLISTERING_COLD_POSITION.GetPositionY());
if (distToSafeSpot > 0.1f) // Avoid division by zero
{
float ratio = std::min(MAX_STEP / distToSafeSpot, 1.0f);
float moveX = bot->GetPositionX() + (ICC_SINDRAGOSA_BLISTERING_COLD_POSITION.GetPositionX() - bot->GetPositionX()) * ratio;
float moveY = bot->GetPositionY() + (ICC_SINDRAGOSA_BLISTERING_COLD_POSITION.GetPositionY() - bot->GetPositionY()) * ratio;
// If we're already at safe distance, no need to move
if (dist >= 30.0f)
return false;
return MoveTo(bot->GetMapId(), moveX, moveY, ICC_SINDRAGOSA_BLISTERING_COLD_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_COMBAT);
// Check boss health to determine movement target
bool isPhaseThree = boss->GetHealthPct() <= 35;
Position const& targetPos = isPhaseThree ? ICC_SINDRAGOSA_LOS2_POSITION : ICC_SINDRAGOSA_BLISTERING_COLD_POSITION;
// Only move if we're too close to the boss (< 30 yards)
if (dist < 30.0f)
{
// If in phase 3, check if already at LOS2 position
if (isPhaseThree && bot->IsWithinDist2d(targetPos.GetPositionX(), targetPos.GetPositionY(), 1.0f))
return false;
float const STEP_SIZE = 10.0f;
float distToTarget = bot->GetDistance2d(targetPos.GetPositionX(), targetPos.GetPositionY());
if (distToTarget > 0.1f) // Avoid division by zero
{
// Calculate direction vector
float dirX = targetPos.GetPositionX() - bot->GetPositionX();
float dirY = targetPos.GetPositionY() - bot->GetPositionY();
// Normalize direction vector
float length = sqrt(dirX * dirX + dirY * dirY);
dirX /= length;
dirY /= length;
// Move STEP_SIZE yards in that direction
float moveX = bot->GetPositionX() + dirX * STEP_SIZE;
float moveY = bot->GetPositionY() + dirY * STEP_SIZE;
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
@@ -2281,35 +2302,11 @@ bool IccSindragosaBlisteringColdAction::Execute(Event event)
bool IccSindragosaUnchainedMagicAction::Execute(Event event)
{
/*if (bot->HasAura(69762)) // unchained magic
{
float const SAFE_DISTANCE = 20.0f;
float moveIncrement = 5.0f;
bool needsToMove = false;
float moveX = bot->GetPositionX();
float moveY = bot->GetPositionY();
// Check distance to other players
GuidVector members = AI_VALUE(GuidVector, "group members");
for (auto& member : members)
{
Unit* unit = botAI->GetUnit(member);
if (!unit || !unit->IsAlive() || unit == bot)
continue;
float dist = bot->GetExactDist2d(unit);
if (dist < SAFE_DISTANCE)
{
needsToMove = true;
float moveDistance = std::min(moveIncrement, SAFE_DISTANCE - dist + 1.0f);
return MoveAway(unit, moveDistance);
}
}
}*/
if (bot->HasAura(69762)) // unchained magic
if (Aura* aura = bot->GetAura(69766))
{
if (aura->GetStackAmount() >= 6)
if (aura->GetStackAmount() >= 3)
return true; // Stop casting spells
}
@@ -2338,37 +2335,35 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event)
// Get boss to check if it exists
Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa");
if (!boss)
return false;
if (!bot || !bot->IsAlive())
if (!boss || !bot || !bot->IsAlive())
return false;
// Check if we have Mystic Buffet
Aura* aura = bot->GetAura(70127);
Aura* aura2 = bot->GetAura(72528);
if (!aura && !aura2)
Aura* aura = botAI->GetAura("mystic buffet", bot, false, true);
if (!aura)
return false;
if (bot->HasAura(70126)) // Ice Block
// Skip if we have Frost Beacon
if (bot->HasAura(70126))
return false;
//if tank and is victim of boss, do nothing
if (botAI->IsTank(bot) && (boss->GetVictim() == bot))
uint8 stacks = aura->GetStackAmount();
// Check if already at LOS2 position
if (bot->IsWithinDist2d(ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(),
ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY(), 1.0f))
{
return false;
}
// For non-tanks, require 3+ stacks
if (!((aura && aura->GetStackAmount() >= 3) || (aura2 && aura2->GetStackAmount() >= 3)))
return false;
// Find nearest player with ice tomb
Unit* nearestTomb = nullptr;
float minDist = 150.0f;
// Find nearest ice tomb
Group* group = bot->GetGroup();
if (!group)
return false;
Unit* nearestTomb = nullptr;
float minDist = 150.0f;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
@@ -2386,30 +2381,17 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event)
}
}
// If no tombs found, return false
if (!nearestTomb)
{
// If no tombs found or tomb not at MB2 position, don't move
if (!nearestTomb || !nearestTomb->IsWithinDist2d(ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionX(),
ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionY(), 1.0f))
return false;
}
if (nearestTomb->IsWithinDist2d(ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionX(),
ICC_SINDRAGOSA_THOMBMB2_POSITION.GetPositionY(), 1.0f))
{
// Check if already at LOS2 position
if (bot->IsWithinDist2d(ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(),
ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY(), 1.0f))
{
return false;
}
// Move to LOS2 position
return MoveTo(bot->GetMapId(),
ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(),
ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY(),
ICC_SINDRAGOSA_LOS2_POSITION.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
}
return false; // Tomb not at MB2 position
false, false, false, false, MovementPriority::MOVEMENT_FORCED);
}
bool IccSindragosaFrostBombAction::Execute(Event event)
@@ -2417,16 +2399,21 @@ bool IccSindragosaFrostBombAction::Execute(Event event)
if (!bot || !bot->IsAlive() || bot->HasAura(70157)) // Skip if dead or in Ice Tomb
return false;
// Find frost bomb marker
Group* group = bot->GetGroup();
if (!group)
return false;
// Find frost bomb marker and tombs in one pass
Unit* marker = nullptr;
Player* nearestTombPlayer = nullptr;
float minDist = 150.0f;
int tombPlayerCount = 0;
Unit* primaryTomb = nullptr;
float highestHealth = 0.0f;
int activeTombCount = 0;
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
if (npcs.empty())
return false;
// Single pass to find marker and tombs
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
@@ -2434,88 +2421,126 @@ bool IccSindragosaFrostBombAction::Execute(Event event)
continue;
if (unit->HasAura(70022)) // Frost bomb visual
{
marker = unit;
break;
}
}
if (!marker)
return false;
// Find players in ice tomb
Group* group = bot->GetGroup();
if (!group)
return false;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member == bot)
continue;
if (member->HasAura(70157)) // Ice Tomb aura
// Check for any ice tomb variant
if (unit->GetEntry() == 36980 || unit->GetEntry() == 38320 ||
unit->GetEntry() == 38321 || unit->GetEntry() == 38322)
{
tombPlayerCount++;
float dist = bot->GetDistance(member);
if (dist < minDist)
activeTombCount++;
if (unit->GetHealthPct() > highestHealth)
{
minDist = dist;
nearestTombPlayer = member;
highestHealth = unit->GetHealthPct();
primaryTomb = unit;
}
}
}
// If no tombs or only one tomb, stop
if (!nearestTombPlayer)
if (!marker || !primaryTomb)
return false;
if (tombPlayerCount <= 1)
// 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();
// Check if we need to move
if (bot->GetDistance2d(posX, posY) > 2.0f)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa");
if (!boss)
return false;
// If boss HP below 20% and we're already not in LOS of marker, stay in position
if (boss->GetHealthPct() < 20 && !marker->IsWithinLOS(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()))
{
bot->AttackStop();
return true;
}
// Otherwise handle normal single tomb case
if (!marker->IsWithinLOS(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()))
{
bot->AttackStop();
return true;
}
// If we have LOS with marker, we need to move behind tomb
float angle = marker->GetAngle(nearestTombPlayer);
float posX = nearestTombPlayer->GetPositionX() + cos(angle) * 5.0f;
float posY = nearestTombPlayer->GetPositionY() + sin(angle) * 5.0f;
float posZ = nearestTombPlayer->GetPositionZ();
return MoveTo(bot->GetMapId(), posX, posY, posZ,
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
}
// Calculate position behind tomb for normal case (more than 1 tomb)
float angle = marker->GetAngle(nearestTombPlayer);
float posX = nearestTombPlayer->GetPositionX() + cos(angle) * 5.0f;
float posY = nearestTombPlayer->GetPositionY() + sin(angle) * 5.0f;
float posZ = nearestTombPlayer->GetPositionZ();
// If we're already in position and not in LOS of marker, stop
if (bot->GetDistance2d(posX, posY) < 2.0f && !marker->IsWithinLOS(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()))
{
bot->AttackStop();
return true;
}
// Otherwise move to position
return MoveTo(bot->GetMapId(), posX, posY, posZ,
false, false, false, true, MovementPriority::MOVEMENT_FORCED);
false, false, false, false, MovementPriority::MOVEMENT_FORCED);
}
// 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
return true;
}
bool IccLichKingShadowTrapAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
if (!boss)
return false;
//search for all nearby traps
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
std::vector<Unit*> nearbyTraps;
bool needToMove = false;
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (!unit || !unit->IsAlive())
continue;
if (unit->GetEntry() == 39137) //shadow trap
{
float distance = bot->GetDistance(unit);
if (distance < 7.0f)
{
needToMove = true;
nearbyTraps.push_back(unit);
}
}
}
if (!needToMove || nearbyTraps.empty())
return false;
// Try different angles to find a safe spot
float const MOVE_DISTANCE = 4.0f;
float const ANGLE_INCREMENT = M_PI / 8; // 22.5 degrees
float bestAngle = 0;
float maxMinDistance = 0;
// Try 16 different directions
for (int i = 0; i < 16; i++)
{
float tryAngle = i * ANGLE_INCREMENT;
float testX = bot->GetPositionX() + cos(tryAngle) * MOVE_DISTANCE;
float testY = bot->GetPositionY() + sin(tryAngle) * MOVE_DISTANCE;
float testZ = 840.857f;
bot->UpdateAllowedPositionZ(testX, testY, testZ);
// Skip invalid positions
if (!bot->IsWithinLOS(testX, testY, testZ))
continue;
// Find minimum distance to any trap from this position
float minTrapDistance = 100.0f;
for (Unit* trap : nearbyTraps)
{
float dx = testX - trap->GetPositionX();
float dy = testY - trap->GetPositionY();
float distToTrap = sqrt(dx * dx + dy * dy);
minTrapDistance = std::min(minTrapDistance, distToTrap);
}
// If this position keeps us further from all traps than previous positions
if (minTrapDistance > maxMinDistance)
{
maxMinDistance = minTrapDistance;
bestAngle = tryAngle;
}
}
// If we found a safe direction, move there
if (maxMinDistance >= 7.0f)
{
float x = bot->GetPositionX() + cos(bestAngle) * MOVE_DISTANCE;
float y = bot->GetPositionY() + sin(bestAngle) * MOVE_DISTANCE;
float z = 840.857f;
bot->UpdateAllowedPositionZ(x, y, z);
return MoveTo(bot->GetMapId(), x, y, z,
false, false, false, false, MovementPriority::MOVEMENT_FORCED);
}
return false;
}
bool IccLichKingNecroticPlagueAction::Execute(Event event)
@@ -2526,39 +2551,61 @@ bool IccLichKingNecroticPlagueAction::Execute(Event event)
if (!hasPlague)
return false;
// Find closest shambling
// Find closest shambling and nearest shadow trap
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
Unit* closestHorror = nullptr;
float minDistance = 100.0f;
Unit* nearestTrap = nullptr;
float minHorrorDist = 100.0f;
float minTrapDist = 100.0f;
for (auto& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (!unit)
continue;
if (!unit->IsAlive())
if (!unit || !unit->IsAlive())
continue;
uint32 entry = unit->GetEntry();
if (entry == 37698 || entry == 39299 || entry == 39300 || entry == 39301) //shambling horror
{
float distance = bot->GetDistance(unit);
if (distance < minDistance)
if (distance < minHorrorDist)
{
minDistance = distance;
minHorrorDist = distance;
closestHorror = unit;
}
}
else if (entry == 39137) //shadow trap
{
float distance = bot->GetDistance(unit);
if (distance < minTrapDist)
{
minTrapDist = distance;
nearestTrap = unit;
}
}
}
// If we found a shambling and we're not close enough, move to it
if (closestHorror)
{
// If we're too far, run to it
if (minDistance > 3.0f)
if (minHorrorDist > 3.0f)
{
// Use forced movement to ensure we get there quickly
// Check if there's a trap in our path
if (nearestTrap && minTrapDist < 9.0f)
{
// Calculate a safe position that avoids the trap
float angle = nearestTrap->GetAngle(closestHorror);
float safeX = nearestTrap->GetPositionX() + cos(angle + M_PI_2) * 10.0f;
float safeY = nearestTrap->GetPositionY() + sin(angle + M_PI_2) * 10.0f;
float safeZ = 840.857f;
// Move to safe position first
return MoveTo(bot->GetMapId(), safeX, safeY, safeZ,
false, false, false, false, MovementPriority::MOVEMENT_FORCED);
}
// No traps nearby, move directly to horror
return MoveTo(closestHorror, 3.0f, MovementPriority::MOVEMENT_FORCED);
}
else
@@ -2673,7 +2720,8 @@ bool IccLichKingAddsAction::Execute(Event event)
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)
if (abs(bot->GetPositionY() - -2095.7915f) > 200.0f)
Difficulty diff = bot->GetRaidDifficulty();
if (!(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) && abs(bot->GetPositionY() - -2095.7915f) > 200.0f)
{
return bot->TeleportTo(bot->GetMapId(), ICC_LICH_KING_ADDS_POSITION.GetPositionX(),
ICC_LICH_KING_ADDS_POSITION.GetPositionY(), ICC_LICH_KING_ADDS_POSITION.GetPositionZ(), bot->GetOrientation());
@@ -2728,7 +2776,11 @@ bool IccLichKingAddsAction::Execute(Event event)
float currentRadius = baseRadius;
Aura* growAura = defile->GetAura(72756);
if (!growAura)
{
growAura = defile->GetAura(74162);
if (!growAura)
growAura = defile->GetAura(74163); //25hc mabye 74164
}
if (growAura)
{
@@ -2872,7 +2924,11 @@ bool IccLichKingAddsAction::Execute(Event event)
float currentRadius = 6.0f;
Aura* growAura = defile->GetAura(72756);
if (!growAura)
{
growAura = defile->GetAura(74162);
if (!growAura)
growAura = defile->GetAura(74163); //25hc mabye 74164
}
if (growAura)
{
@@ -3150,7 +3206,7 @@ bool IccLichKingAddsAction::Execute(Event event)
if (botAI->IsRanged(bot) && !boss->HealthBelowPct(71))
{
float currentDist = bot->GetDistance(ICC_LICH_KING_ADDS_POSITION);
if (currentDist < 21.0f || currentDist > 35.0f) // 20 yards ±3 yards tolerance
if (currentDist < 21.0f) // 20 yards ±3 yards tolerance
{
float angle = ICC_LICH_KING_ADDS_POSITION.GetAngle(bot);
float targetDist = 26.0f;

View File

@@ -41,7 +41,7 @@ const Position ICC_BQL_TANK_POSITION = Position(4616.102f, 2768.9167f, 400.13797
const Position ICC_SINDRAGOSA_TANK_POSITION = Position(4408.016f, 2508.0647f, 203.37955f);
const Position ICC_SINDRAGOSA_RANGED_POSITION = Position(4373.7686f, 2498.0042f, 203.38176f);
const Position ICC_SINDRAGOSA_MELEE_POSITION = Position(4389.22f, 2499.5237f, 203.38033f);
const Position ICC_SINDRAGOSA_BLISTERING_COLD_POSITION = Position(4357.036f, 2484.5574f, 203.4777f);
const Position ICC_SINDRAGOSA_BLISTERING_COLD_POSITION = Position(4345.922f, 2484.708f, 206.22516f);
const Position ICC_SINDRAGOSA_THOMB1_POSITION = Position(4381.819f, 2471.1448f, 203.37704f); // Westmost position
const Position ICC_SINDRAGOSA_THOMB2_POSITION = Position(4381.819f, 2483.1448f, 203.37704f); // 12y east from pos1
const Position ICC_SINDRAGOSA_THOMB3_POSITION = Position(4381.819f, 2471.1448f, 203.37704f); // Same as pos1
@@ -49,7 +49,7 @@ const Position ICC_SINDRAGOSA_THOMB4_POSITION = Position(4381.819f, 2483.1448f,
const Position ICC_SINDRAGOSA_THOMB5_POSITION = Position(4381.819f, 2495.1448f, 203.37704f); // 12y east from pos2/4
const Position ICC_SINDRAGOSA_CENTER_POSITION = Position(4408.0464f, 2484.478f, 203.37529f);
const Position ICC_SINDRAGOSA_THOMBMB2_POSITION = Position(4382.6113f, 2505.4922f, 203.38197f);
const Position ICC_SINDRAGOSA_FBOMB_POSITION = Position(4400.031f, 2507.0295f, 203.37929f);
const Position ICC_SINDRAGOSA_FBOMB_POSITION = Position(4366.0225f, 2501.569f, 203.38226f); //old 4400.031f, 2507.0295f, 203.37929f
const Position ICC_SINDRAGOSA_LOS2_POSITION = Position(4376.0938f, 2511.103f, 203.38303f);
const Position ICC_LICH_KING_ADDS_POSITION = Position(486.63647f, -2095.7915f, 840.857f);
const Position ICC_LK_FROST1_POSITION = Position(503.96548f, -2183.216f, 840.857f);
@@ -353,19 +353,19 @@ public:
bool Execute(Event event) override;
};
class IccSindragosaFrostBeaconAction : public AttackAction
class IccSindragosaFrostBeaconAction : public MovementAction
{
public:
IccSindragosaFrostBeaconAction(PlayerbotAI* botAI)
: AttackAction(botAI, "icc sindragosa frost beacon") {}
: MovementAction(botAI, "icc sindragosa frost beacon") {}
bool Execute(Event event) override;
};
class IccSindragosaBlisteringColdAction : public AttackAction
class IccSindragosaBlisteringColdAction : public MovementAction
{
public:
IccSindragosaBlisteringColdAction(PlayerbotAI* botAI)
: AttackAction(botAI, "icc sindragosa blistering cold") {}
: MovementAction(botAI, "icc sindragosa blistering cold") {}
bool Execute(Event event) override;
};
@@ -393,11 +393,11 @@ public:
bool Execute(Event event) override;
};
class IccSindragosaFrostBombAction : public AttackAction
class IccSindragosaFrostBombAction : public MovementAction
{
public:
IccSindragosaFrostBombAction(PlayerbotAI* botAI)
: AttackAction(botAI, "icc sindragosa frost bomb") {}
: MovementAction(botAI, "icc sindragosa frost bomb") {}
bool Execute(Event event) override;
};
@@ -411,6 +411,14 @@ class IccSindragosaTankSwapPositionAction : public AttackAction
//LK
class IccLichKingShadowTrapAction : public MovementAction
{
public:
IccLichKingShadowTrapAction(PlayerbotAI* botAI)
: MovementAction(botAI, "icc lich king shadow trap") {}
bool Execute(Event event) override;
};
class IccLichKingNecroticPlagueAction : public MovementAction
{
public:

View File

@@ -513,8 +513,6 @@ float IccSindragosaMysticBuffetMultiplier::GetValue(Action* action)
float IccSindragosaFrostBombMultiplier::GetValue(Action* action)
{
if (!action || !bot || !bot->IsAlive())
return 1.0f;
Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa");
if (!boss)
@@ -552,7 +550,7 @@ 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<FollowAction*>(action) || dynamic_cast<AttackAction*>(action))
return 0.0f;
return 1.0f;
}

View File

@@ -158,6 +158,9 @@ void RaidIccStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
NextAction::array(0, new NextAction("icc sindragosa tank swap position", ACTION_EMERGENCY + 2), nullptr)));
//LICH KING
triggers.push_back(new TriggerNode("icc lich king shadow trap",
NextAction::array(0, new NextAction("icc lich king shadow trap", ACTION_RAID + 6), nullptr)));
triggers.push_back(new TriggerNode("icc lich king necrotic plague",
NextAction::array(0, new NextAction("icc lich king necrotic plague", ACTION_RAID + 3), nullptr)));

View File

@@ -57,6 +57,7 @@ public:
creators["icc sindragosa main tank mystic buffet"] = &RaidIccTriggerContext::icc_sindragosa_main_tank_mystic_buffet;
creators["icc sindragosa frost bomb"] = &RaidIccTriggerContext::icc_sindragosa_frost_bomb;
creators["icc sindragosa tank swap position"] = &RaidIccTriggerContext::icc_sindragosa_tank_swap_position;
creators["icc lich king shadow trap"] = &RaidIccTriggerContext::icc_lich_king_shadow_trap;
creators["icc lich king necrotic plague"] = &RaidIccTriggerContext::icc_lich_king_necrotic_plague;
creators["icc lich king winter"] = &RaidIccTriggerContext::icc_lich_king_winter;
creators["icc lich king adds"] = &RaidIccTriggerContext::icc_lich_king_adds;
@@ -110,6 +111,7 @@ private:
static Trigger* icc_sindragosa_main_tank_mystic_buffet(PlayerbotAI* ai) { return new IccSindragosaMainTankMysticBuffetTrigger(ai); }
static Trigger* icc_sindragosa_frost_bomb(PlayerbotAI* ai) { return new IccSindragosaFrostBombTrigger(ai); }
static Trigger* icc_sindragosa_tank_swap_position(PlayerbotAI* ai) { return new IccSindragosaTankSwapPositionTrigger(ai); }
static Trigger* icc_lich_king_shadow_trap(PlayerbotAI* ai) { return new IccLichKingShadowTrapTrigger(ai); }
static Trigger* icc_lich_king_necrotic_plague(PlayerbotAI* ai) { return new IccLichKingNecroticPlagueTrigger(ai); }
static Trigger* icc_lich_king_winter(PlayerbotAI* ai) { return new IccLichKingWinterTrigger(ai); }
static Trigger* icc_lich_king_adds(PlayerbotAI* ai) { return new IccLichKingAddsTrigger(ai); }

View File

@@ -551,6 +551,10 @@ bool IccValithriaPortalTrigger::IsActive()
bool IccValithriaHealTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "valithria dreamwalker");
if (!boss)
return false;
// Only healers should use healing
if (!botAI->IsHeal(bot) || bot->HasAura(70766))
return false;
@@ -600,6 +604,9 @@ bool IccSindragosaFrostBeaconTrigger::IsActive()
if (!group)
return false;
if (boss->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY) && !boss->HealthBelowPct(35))
return true;
Group::MemberSlotList const& groupSlot = group->GetMemberSlots();
for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); ++itr)
{
@@ -619,7 +626,7 @@ bool IccSindragosaBlisteringColdTrigger::IsActive()
if (!boss)
return false;
if (botAI->IsMainTank(bot) || botAI->IsAssistTank(bot) || botAI->IsTank(bot))
if (botAI->IsMainTank(bot))
return false;
// Don't move if any bot in group has ice tomb
@@ -627,6 +634,11 @@ bool IccSindragosaBlisteringColdTrigger::IsActive()
if (!group)
return false;
float dist = bot->GetDistance(boss);
if (dist >= 30.0f)
return false;
bool isCasting = boss->HasUnitState(UNIT_STATE_CASTING);
bool isBlisteringCold = boss->FindCurrentSpellBySpellId(70123) || boss->FindCurrentSpellBySpellId(71047) ||
boss->FindCurrentSpellBySpellId(71048) || boss->FindCurrentSpellBySpellId(71049);
@@ -658,15 +670,20 @@ bool IccSindragosaMysticBuffetTrigger::IsActive()
if (!boss)
return false;
Aura* aura = bot->GetAura(70127);
Aura* aura2 = bot->GetAura(72528);
if (!aura && !aura2)
if (botAI->IsTank(bot) && boss->GetVictim() == bot)
return false;
if (bot->HasAura(70126)) // Ice Block
Aura* aura = botAI->GetAura("mystic buffet", bot, false, true);
if (!aura)
return false;
if ((aura && aura->GetStackAmount() >= 3) || (aura2 && aura2->GetStackAmount() >= 3))
if (bot->HasAura(70126)) // Frost Beacon
return false;
if (aura->GetStackAmount() >= 2 && botAI->IsAssistTank(bot))
return true;
if (aura->GetStackAmount() >= 3)
return true;
return false;
@@ -678,27 +695,35 @@ bool IccSindragosaMainTankMysticBuffetTrigger::IsActive()
if (!boss)
return false;
if (!botAI->IsAssistTankOfIndex(bot, 0))
{
if (botAI->IsTank(bot) && boss->GetVictim() == bot)
return false;
}
// Only for assist tank
if (!botAI->IsAssistTankOfIndex(bot, 0))
return false;
// Don't swap if we have frost beacon
if (bot->HasAura(70126)) // Frost Beacon
return false;
Unit* mt = AI_VALUE(Unit*, "main tank");
if (!mt)
{
return false;
}
Aura* aura = botAI->GetAura("mystic buffet", mt, false, true);
if (!aura || aura->GetStackAmount() < 8)
{
// Check main tank stacks
Aura* mtAura = botAI->GetAura("mystic buffet", mt, false, true);
if (!mtAura || mtAura->GetStackAmount() < 9)
return false;
// Check our own stacks - don't taunt if we have too many
Aura* selfAura = botAI->GetAura("mystic buffet", bot, false, true);
if (selfAura && selfAura->GetStackAmount() > 6)
return false;
}
// Only taunt if we're in position
float distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION);
if (distToTankPos > 3.0f)
{
return false;
}
return true;
}
@@ -706,26 +731,41 @@ bool IccSindragosaMainTankMysticBuffetTrigger::IsActive()
bool IccSindragosaTankSwapPositionTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa");
if (!boss) return false;
if (!boss)
return false;
if (!botAI->IsAssistTankOfIndex(bot, 0)) return false;
// Only for assist tank
if (!botAI->IsAssistTankOfIndex(bot, 0))
return false;
// Don't tank swap if we have frost beacon
if (bot->HasAura(70126)) return false; // Frost Beacon
// Don't move to position if we have frost beacon
if (bot->HasAura(70126)) // Frost Beacon
return false;
// First check our own stacks - don't try to tank if we have too many
// Check our own stacks - don't try to tank if we have too many
Aura* selfAura = botAI->GetAura("mystic buffet", bot, false, true);
if (selfAura && selfAura->GetStackAmount() >= 8) return false;
if (selfAura && selfAura->GetStackAmount() > 6)
return false;
// Check if main tank has high stacks
Unit* mt = AI_VALUE(Unit*, "main tank");
if (!mt) return false;
if (!mt)
return false;
Aura* aura = botAI->GetAura("mystic buffet", mt, false, true);
if (!aura) return false;
Aura* mtAura = botAI->GetAura("mystic buffet", mt, false, true);
if (!mtAura)
return false;
uint32 stacks = aura->GetStackAmount();
return (stacks >= 7); // Start moving at 7 stacks
uint32 mtStacks = mtAura->GetStackAmount();
if (mtStacks < 9) // Only start moving when MT has 5+ stacks
return false;
// Check if we're already in position
float distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION);
if (distToTankPos <= 3.0f)
return false;
return true; // Move to position if all conditions are met
}
bool IccSindragosaFrostBombTrigger::IsActive()
@@ -748,6 +788,17 @@ bool IccSindragosaFrostBombTrigger::IsActive()
return false;
}
//LK
bool IccLichKingShadowTrapTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king");
if (!boss)
return false;
return true;
}
bool IccLichKingNecroticPlagueTrigger::IsActive()
{
if (!bot || !bot->IsAlive())

View File

@@ -351,10 +351,17 @@ public:
//LICH KING
class IccLichKingShadowTrapTrigger : public Trigger
{
public:
IccLichKingShadowTrapTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king shadow trap") {}
bool IsActive() override;
};
class IccLichKingNecroticPlagueTrigger : public Trigger
{
public:
IccLichKingNecroticPlagueTrigger(PlayerbotAI* ai) : Trigger(ai, "icc lich king necrotic plague") {}
IccLichKingNecroticPlagueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king necrotic plague") {}
bool IsActive() override;
};