mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-13 23:33:47 +00:00
Compare commits
16 Commits
hermensbas
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6a6af1b4d | ||
|
|
610fdc16d7 | ||
|
|
c9c936d5c1 | ||
|
|
cfb2ed4bf3 | ||
|
|
e9e79ad696 | ||
|
|
3db2a5a193 | ||
|
|
8585f10f48 | ||
|
|
79fb3a5bbc | ||
|
|
6ed3f24ecb | ||
|
|
76b6df9ea3 | ||
|
|
026df0dabe | ||
|
|
b31bda85ee | ||
|
|
bebac60c51 | ||
|
|
52d4191b43 | ||
|
|
254055ff32 | ||
|
|
31765c77fa |
4
.github/workflows/check_pr_source.yml
vendored
4
.github/workflows/check_pr_source.yml
vendored
@@ -1,13 +1,15 @@
|
||||
name: Enforce test-staging → main
|
||||
name: Enforce test-staging → master
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- test-staging
|
||||
|
||||
jobs:
|
||||
require-test-staging:
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event.pull_request.base.ref == 'master'
|
||||
steps:
|
||||
- name: Ensure PR source is test-staging
|
||||
run: |
|
||||
|
||||
4
.github/workflows/code_style.yml
vendored
4
.github/workflows/code_style.yml
vendored
@@ -2,9 +2,9 @@ name: Codestyle
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
|
||||
concurrency:
|
||||
group: "codestyle-${{ github.event.pull_request.number }}"
|
||||
|
||||
4
.github/workflows/core_build.yml
vendored
4
.github/workflows/core_build.yml
vendored
@@ -2,9 +2,9 @@ name: ubuntu-build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
|
||||
concurrency:
|
||||
group: "core-build-${{ github.event.pull_request.number }}"
|
||||
|
||||
4
.github/workflows/macos_build.yml
vendored
4
.github/workflows/macos_build.yml
vendored
@@ -1,9 +1,9 @@
|
||||
name: macos-build
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
|
||||
concurrency:
|
||||
group: "macos-build-${{ github.event.pull_request.number }}"
|
||||
|
||||
4
.github/workflows/windows_build.yml
vendored
4
.github/workflows/windows_build.yml
vendored
@@ -1,9 +1,9 @@
|
||||
name: windows-build
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "master", "test-staging" ]
|
||||
|
||||
concurrency:
|
||||
group: "windows-build-${{ github.event.pull_request.number }}"
|
||||
|
||||
@@ -66,38 +66,35 @@ Please answer the following:
|
||||
|
||||
## Complexity & Impact
|
||||
|
||||
- Does this change add new decision branches?
|
||||
- [ ] No
|
||||
- [ ] Yes (**explain below**)
|
||||
Does this change add new decision branches?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain below**)
|
||||
|
||||
- Does this change increase per-bot or per-tick processing?
|
||||
- [ ] No
|
||||
- [ ] Yes (**describe and justify impact**)
|
||||
|
||||
- Could this logic scale poorly under load?
|
||||
- [ ] No
|
||||
- [ ] Yes (**explain why**)
|
||||
Does this change increase per-bot or per-tick processing?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**describe and justify impact**)
|
||||
|
||||
Could this logic scale poorly under load?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain why**)
|
||||
---
|
||||
|
||||
## Defaults & Configuration
|
||||
|
||||
- Does this change modify default bot behavior?
|
||||
- [ ] No
|
||||
- [ ] Yes (**explain why**)
|
||||
Does this change modify default bot behavior?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain why**)
|
||||
|
||||
If this introduces more advanced or AI-heavy logic:
|
||||
|
||||
- [ ] Lightweight mode remains the default
|
||||
- [ ] More complex behavior is optional and thereby configurable
|
||||
|
||||
- - [ ] Lightweight mode remains the default
|
||||
- - [ ] More complex behavior is optional and thereby configurable
|
||||
---
|
||||
|
||||
## AI Assistance
|
||||
|
||||
- Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change?
|
||||
- [ ] No
|
||||
- [ ] Yes (**explain below**)
|
||||
Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change?
|
||||
- - [ ] No
|
||||
- - [ ] Yes (**explain below**)
|
||||
|
||||
If yes, please specify:
|
||||
|
||||
@@ -114,10 +111,10 @@ about what they do and do not understand.
|
||||
|
||||
## Final Checklist
|
||||
|
||||
- [ ] Stability is not compromised
|
||||
- [ ] Performance impact is understood, tested, and acceptable
|
||||
- [ ] Added logic complexity is justified and explained
|
||||
- [ ] Documentation updated if needed
|
||||
- - [ ] Stability is not compromised
|
||||
- - [ ] Performance impact is understood, tested, and acceptable
|
||||
- - [ ] Added logic complexity is justified and explained
|
||||
- - [ ] Documentation updated if needed
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -558,7 +558,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
|
||||
# "mana" (bots have infinite mana)
|
||||
# "power" (bots have infinite energy, rage, and runic power)
|
||||
# "taxi" (bots may use all flight paths, though they will not actually learn them)
|
||||
# "raid" (bots use cheats implemented into raid strategies (currently only for Ulduar))
|
||||
# "raid" (bots use cheats implemented into raid strategies (currently only for SSC and Ulduar))
|
||||
# To use multiple cheats, separate them by commas below (e.g., to enable all, use "gold,health,mana,power,raid,taxi")
|
||||
# Default: food, taxi, and raid are enabled
|
||||
AiPlayerbot.BotCheats = "food,taxi,raid"
|
||||
@@ -990,7 +990,7 @@ AiPlayerbot.ZoneBracket.3433 = 10,22
|
||||
AiPlayerbot.ZoneBracket.3525 = 10,21
|
||||
|
||||
# Classic WoW - High-level zones:
|
||||
# Deadwind Pass (Zone ID: 10 Default Min,Max: 19,33)
|
||||
# Duskwood (Zone ID: 10 Default Min,Max: 19,33)
|
||||
# Wetlands (Zone ID: 11 Default Min,Max: 21,30)
|
||||
# Redridge Mountains (Zone ID: 44 Default Min,Max: 16,28)
|
||||
# Hillsbrad Foothills (Zone ID: 267 Default Min,Max: 20,34)
|
||||
|
||||
@@ -328,7 +328,43 @@ void EquipAction::EquipItem(Item* item)
|
||||
botAI->TellMaster(out);
|
||||
}
|
||||
|
||||
bool EquipUpgradesAction::Execute(Event event)
|
||||
ItemIds EquipAction::SelectInventoryItemsToEquip()
|
||||
{
|
||||
CollectItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
|
||||
ItemIds items;
|
||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
{
|
||||
Item* item = *i;
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
ItemTemplate const* itemTemplate = item->GetTemplate();
|
||||
if (!itemTemplate)
|
||||
continue;
|
||||
|
||||
//TODO Expand to Glyphs and Gems, that can be placed in equipment
|
||||
//Pre-filter non-equipable items
|
||||
if (itemTemplate->InventoryType == INVTYPE_NON_EQUIP)
|
||||
continue;
|
||||
|
||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||
uint32 itemId = item->GetTemplate()->ItemId;
|
||||
std::string itemUsageParam;
|
||||
if (randomProperty != 0)
|
||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||
else
|
||||
itemUsageParam = std::to_string(itemId);
|
||||
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item upgrade", itemUsageParam);
|
||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
||||
items.insert(itemId);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
bool EquipUpgradesTriggeredAction::Execute(Event event)
|
||||
{
|
||||
if (!sPlayerbotAIConfig.autoEquipUpgradeLoot && !sRandomPlayerbotMgr.IsRandomBot(bot))
|
||||
return false;
|
||||
@@ -361,72 +397,18 @@ bool EquipUpgradesAction::Execute(Event event)
|
||||
p >> itemId;
|
||||
|
||||
ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (item->Class == ITEM_CLASS_TRADE_GOODS && item->SubClass == ITEM_SUBCLASS_MEAT)
|
||||
if (item->InventoryType == INVTYPE_NON_EQUIP)
|
||||
return false;
|
||||
}
|
||||
|
||||
CollectItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
|
||||
ItemIds items;
|
||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
{
|
||||
Item* item = *i;
|
||||
if (!item)
|
||||
break;
|
||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||
uint32 itemId = item->GetTemplate()->ItemId;
|
||||
std::string itemUsageParam;
|
||||
if (randomProperty != 0)
|
||||
{
|
||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemUsageParam = std::to_string(itemId);
|
||||
}
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||
|
||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
||||
{
|
||||
items.insert(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
ItemIds items = SelectInventoryItemsToEquip();
|
||||
EquipItems(items);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EquipUpgradeAction::Execute(Event event)
|
||||
{
|
||||
CollectItemsVisitor visitor;
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
|
||||
ItemIds items;
|
||||
for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i)
|
||||
{
|
||||
Item* item = *i;
|
||||
if (!item)
|
||||
break;
|
||||
int32 randomProperty = item->GetItemRandomPropertyId();
|
||||
uint32 itemId = item->GetTemplate()->ItemId;
|
||||
std::string itemUsageParam;
|
||||
if (randomProperty != 0)
|
||||
{
|
||||
itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemUsageParam = std::to_string(itemId);
|
||||
}
|
||||
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam);
|
||||
|
||||
if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
|
||||
{
|
||||
items.insert(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
ItemIds items = SelectInventoryItemsToEquip();
|
||||
EquipItems(items);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "ChatHelper.h"
|
||||
#include "InventoryAction.h"
|
||||
#include "Item.h"
|
||||
|
||||
class FindItemVisitor;
|
||||
class Item;
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
|
||||
bool Execute(Event event) override;
|
||||
void EquipItems(ItemIds ids);
|
||||
ItemIds SelectInventoryItemsToEquip();
|
||||
|
||||
private:
|
||||
void EquipItem(FindItemVisitor* visitor);
|
||||
@@ -27,10 +29,10 @@ private:
|
||||
void EquipItem(Item* item);
|
||||
};
|
||||
|
||||
class EquipUpgradesAction : public EquipAction
|
||||
class EquipUpgradesTriggeredAction : public EquipAction
|
||||
{
|
||||
public:
|
||||
EquipUpgradesAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {}
|
||||
explicit EquipUpgradesTriggeredAction(PlayerbotAI* botAI, std::string const name = "equip upgrades") : EquipAction(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
@@ -38,7 +40,7 @@ public:
|
||||
class EquipUpgradeAction : public EquipAction
|
||||
{
|
||||
public:
|
||||
EquipUpgradeAction(PlayerbotAI* botAI, std::string const name = "equip upgrade") : EquipAction(botAI, name) {}
|
||||
explicit EquipUpgradeAction(PlayerbotAI* botAI, std::string const name = "equip upgrade") : EquipAction(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
@@ -90,6 +90,8 @@ bool LootRollAction::Execute(Event event)
|
||||
}
|
||||
else if (sPlayerbotAIConfig.lootRollLevel == 1)
|
||||
{
|
||||
// Level 1 = "greed" mode: bots greed on useful items but never need
|
||||
// Only downgrade NEED to GREED, preserve GREED votes as-is
|
||||
if (vote == NEED)
|
||||
{
|
||||
if (RollUniqueCheck(proto, bot))
|
||||
@@ -101,10 +103,6 @@ bool LootRollAction::Execute(Event event)
|
||||
vote = GREED;
|
||||
}
|
||||
}
|
||||
else if (vote == GREED)
|
||||
{
|
||||
vote = PASS;
|
||||
}
|
||||
}
|
||||
switch (group->GetLootMethod())
|
||||
{
|
||||
|
||||
@@ -17,9 +17,9 @@ public:
|
||||
SummonAction(PlayerbotAI* botAI, std::string const name = "summon") : MovementAction(botAI, name) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
|
||||
|
||||
protected:
|
||||
bool Teleport(Player* summoner, Player* player, bool preserveAuras);
|
||||
bool SummonUsingGos(Player* summoner, Player* player, bool preserveAuras);
|
||||
bool SummonUsingNpcs(Player* summoner, Player* player, bool preserveAuras);
|
||||
};
|
||||
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
creators["use"] = &ChatActionContext::use;
|
||||
creators["item count"] = &ChatActionContext::item_count;
|
||||
creators["equip"] = &ChatActionContext::equip;
|
||||
creators["equip upgrades"] = &ChatActionContext::equip_upgrades;
|
||||
creators["equip upgrades"] = &ChatActionContext::equip_upgrade;
|
||||
creators["unequip"] = &ChatActionContext::unequip;
|
||||
creators["sell"] = &ChatActionContext::sell;
|
||||
creators["buy"] = &ChatActionContext::buy;
|
||||
@@ -258,7 +258,6 @@ private:
|
||||
static Action* talents(PlayerbotAI* botAI) { return new ChangeTalentsAction(botAI); }
|
||||
|
||||
static Action* equip(PlayerbotAI* botAI) { return new EquipAction(botAI); }
|
||||
static Action* equip_upgrades(PlayerbotAI* botAI) { return new EquipUpgradesAction(botAI); }
|
||||
static Action* unequip(PlayerbotAI* botAI) { return new UnequipAction(botAI); }
|
||||
static Action* sell(PlayerbotAI* botAI) { return new SellAction(botAI); }
|
||||
static Action* buy(PlayerbotAI* botAI) { return new BuyAction(botAI); }
|
||||
|
||||
@@ -42,6 +42,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
NextAction("query item usage", relevance),
|
||||
NextAction("equip upgrades", relevance) }));
|
||||
triggers.push_back(new TriggerNode("item push result", { NextAction("quest item push result", relevance) }));
|
||||
triggers.push_back(new TriggerNode("loot roll won", { NextAction("equip upgrades", relevance) }));
|
||||
triggers.push_back(new TriggerNode("ready check finished", { NextAction("finish ready check", relevance) }));
|
||||
// triggers.push_back(new TriggerNode("often", { NextAction("security check", relevance), NextAction("check mail", relevance) }));
|
||||
triggers.push_back(new TriggerNode("guild invite", { NextAction("guild accept", relevance) }));
|
||||
|
||||
@@ -19,19 +19,9 @@
|
||||
|
||||
ItemUsage ItemUsageValue::Calculate()
|
||||
{
|
||||
uint32 itemId = 0;
|
||||
uint32 randomPropertyId = 0;
|
||||
size_t pos = qualifier.find(",");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
itemId = atoi(qualifier.substr(0, pos).c_str());
|
||||
randomPropertyId = atoi(qualifier.substr(pos + 1).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
itemId = atoi(qualifier.c_str());
|
||||
}
|
||||
|
||||
ParsedItemUsage const parsed = GetItemIdFromQualifier();
|
||||
uint32 itemId = parsed.itemId;
|
||||
uint32 randomPropertyId = parsed.randomPropertyId;
|
||||
if (!itemId)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
@@ -142,96 +132,30 @@ ItemUsage ItemUsageValue::Calculate()
|
||||
|
||||
// If the loot is from an item in the bot’s bags, ignore syncQuestWithPlayer
|
||||
if (isLootFromItem && botNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_QUEST;
|
||||
}
|
||||
|
||||
// If the bot is NOT acting alone and the master needs this quest item, defer to the master
|
||||
if (!isSelfBot && masterNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
// If the bot itself needs the item for a quest, allow looting
|
||||
if (botNeedsItemForQuest)
|
||||
{
|
||||
return ITEM_USAGE_QUEST;
|
||||
}
|
||||
|
||||
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||
{
|
||||
if (bot->getClass() == CLASS_HUNTER || bot->getClass() == CLASS_ROGUE || bot->getClass() == CLASS_WARRIOR)
|
||||
{
|
||||
Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||
uint32 requiredSubClass = 0;
|
||||
|
||||
if (rangedWeapon)
|
||||
{
|
||||
switch (rangedWeapon->GetTemplate()->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
requiredSubClass = ITEM_SUBCLASS_BULLET;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
requiredSubClass = ITEM_SUBCLASS_ARROW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the item is the correct ammo type for the equipped ranged weapon
|
||||
if (proto->SubClass == requiredSubClass)
|
||||
{
|
||||
float ammoCount = BetterStacks(proto, "ammo");
|
||||
float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2
|
||||
uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID);
|
||||
|
||||
// Check if the bot has an ammo type assigned
|
||||
if (currentAmmoId == 0)
|
||||
{
|
||||
return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo
|
||||
}
|
||||
// Compare new ammo vs current equipped ammo
|
||||
ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId);
|
||||
if (currentAmmoProto)
|
||||
{
|
||||
uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2;
|
||||
uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2;
|
||||
|
||||
if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition
|
||||
{
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
if (newAmmoDPS < currentAmmoDPS) // New ammo is worse
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
// Ensure we have enough ammo in the inventory
|
||||
if (ammoCount < requiredAmmo)
|
||||
{
|
||||
ammoCount += CurrentStacks(proto);
|
||||
|
||||
if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply
|
||||
return ITEM_USAGE_AMMO;
|
||||
else if (ammoCount < requiredAmmo + 1)
|
||||
return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much.
|
||||
}
|
||||
}
|
||||
}
|
||||
ItemUsage ammoUsage = QueryItemUsageForAmmo(proto);
|
||||
if (ammoUsage != ITEM_USAGE_NONE)
|
||||
return ammoUsage;
|
||||
}
|
||||
|
||||
// Need to add something like free bagspace or item value.
|
||||
if (proto->SellPrice > 0)
|
||||
{
|
||||
if (proto->Quality >= ITEM_QUALITY_NORMAL && !isSoulbound)
|
||||
{
|
||||
return ITEM_USAGE_AH;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return ITEM_USAGE_VENDOR;
|
||||
}
|
||||
}
|
||||
|
||||
return ITEM_USAGE_NONE;
|
||||
@@ -480,6 +404,80 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
ItemUsage ItemUsageValue::QueryItemUsageForAmmo(ItemTemplate const* proto)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER || bot->getClass() != CLASS_ROGUE || bot->getClass() != CLASS_WARRIOR)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
|
||||
uint32 requiredSubClass = 0;
|
||||
|
||||
if (rangedWeapon)
|
||||
{
|
||||
switch (rangedWeapon->GetTemplate()->SubClass)
|
||||
{
|
||||
case ITEM_SUBCLASS_WEAPON_GUN:
|
||||
requiredSubClass = ITEM_SUBCLASS_BULLET;
|
||||
break;
|
||||
case ITEM_SUBCLASS_WEAPON_BOW:
|
||||
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
|
||||
requiredSubClass = ITEM_SUBCLASS_ARROW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the item is the correct ammo type for the equipped ranged weapon
|
||||
if (proto->SubClass == requiredSubClass)
|
||||
{
|
||||
float ammoCount = BetterStacks(proto, "ammo");
|
||||
float requiredAmmo = (bot->getClass() == CLASS_HUNTER) ? 8 : 2; // Hunters get 8 stacks, others 2
|
||||
uint32 currentAmmoId = bot->GetUInt32Value(PLAYER_AMMO_ID);
|
||||
|
||||
// Check if the bot has an ammo type assigned
|
||||
if (currentAmmoId == 0)
|
||||
return ITEM_USAGE_EQUIP; // Equip the ammo if no ammo
|
||||
// Compare new ammo vs current equipped ammo
|
||||
ItemTemplate const* currentAmmoProto = sObjectMgr->GetItemTemplate(currentAmmoId);
|
||||
if (currentAmmoProto)
|
||||
{
|
||||
uint32 currentAmmoDPS = (currentAmmoProto->Damage[0].DamageMin + currentAmmoProto->Damage[0].DamageMax) * 1000 / 2;
|
||||
uint32 newAmmoDPS = (proto->Damage[0].DamageMin + proto->Damage[0].DamageMax) * 1000 / 2;
|
||||
|
||||
if (newAmmoDPS > currentAmmoDPS) // New ammo meets upgrade condition
|
||||
return ITEM_USAGE_EQUIP;
|
||||
|
||||
if (newAmmoDPS < currentAmmoDPS) // New ammo is worse
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
// Ensure we have enough ammo in the inventory
|
||||
if (ammoCount < requiredAmmo)
|
||||
{
|
||||
ammoCount += CurrentStacks(proto);
|
||||
|
||||
if (ammoCount < requiredAmmo) // Buy ammo to reach the proper supply
|
||||
return ITEM_USAGE_AMMO;
|
||||
else if (ammoCount < requiredAmmo + 1)
|
||||
return ITEM_USAGE_KEEP; // Keep the ammo if we don't have too much.
|
||||
}
|
||||
}
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
ParsedItemUsage ItemUsageValue::GetItemIdFromQualifier()
|
||||
{
|
||||
ParsedItemUsage parsed;
|
||||
|
||||
size_t const pos = qualifier.find(",");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
parsed.itemId = atoi(qualifier.substr(0, pos).c_str());
|
||||
parsed.randomPropertyId = atoi(qualifier.substr(pos + 1).c_str());
|
||||
return parsed;
|
||||
}
|
||||
else
|
||||
parsed.itemId = atoi(qualifier.c_str());
|
||||
return parsed;
|
||||
}
|
||||
// Return smaltest bag size equipped
|
||||
uint32 ItemUsageValue::GetSmallestBagSize()
|
||||
{
|
||||
@@ -913,3 +911,25 @@ std::string const ItemUsageValue::GetConsumableType(ItemTemplate const* proto, b
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
ItemUsage ItemUpgradeValue::Calculate()
|
||||
{
|
||||
ParsedItemUsage parsed = GetItemIdFromQualifier();
|
||||
uint32 itemId = parsed.itemId;
|
||||
uint32 randomPropertyId = parsed.randomPropertyId;
|
||||
if (!itemId)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId);
|
||||
if (equip != ITEM_USAGE_NONE)
|
||||
return equip;
|
||||
|
||||
if (proto->Class == ITEM_CLASS_PROJECTILE && bot->CanUseItem(proto) == EQUIP_ERR_OK)
|
||||
return QueryItemUsageForAmmo(proto);
|
||||
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,11 @@ class Player;
|
||||
class PlayerbotAI;
|
||||
|
||||
struct ItemTemplate;
|
||||
|
||||
struct ParsedItemUsage
|
||||
{
|
||||
uint32 itemId = 0;
|
||||
int32 randomPropertyId = 0;
|
||||
};
|
||||
enum ItemUsage : uint32
|
||||
{
|
||||
ITEM_USAGE_NONE = 0,
|
||||
@@ -42,8 +46,12 @@ public:
|
||||
|
||||
ItemUsage Calculate() override;
|
||||
|
||||
private:
|
||||
protected:
|
||||
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0);
|
||||
ItemUsage QueryItemUsageForAmmo(ItemTemplate const* proto);
|
||||
ParsedItemUsage GetItemIdFromQualifier();
|
||||
|
||||
private:
|
||||
uint32 GetSmallestBagSize();
|
||||
bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto);
|
||||
bool IsItemNeededForSkill(ItemTemplate const* proto);
|
||||
@@ -61,4 +69,14 @@ public:
|
||||
static std::string const GetConsumableType(ItemTemplate const* proto, bool hasMana);
|
||||
};
|
||||
|
||||
class ItemUpgradeValue : public ItemUsageValue
|
||||
{
|
||||
public:
|
||||
ItemUpgradeValue(PlayerbotAI* botAI, std::string const name = "item upgrade") : ItemUsageValue(botAI, name)
|
||||
{
|
||||
}
|
||||
|
||||
ItemUsage Calculate() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -173,7 +173,7 @@ std::vector<GuidPosition> ActiveQuestGiversValue::Calculate()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (guidp.isDead())
|
||||
if (!guidp.IsCreatureOrGOAccessible())
|
||||
continue;
|
||||
|
||||
retQuestGivers.push_back(guidp);
|
||||
@@ -231,7 +231,7 @@ std::vector<GuidPosition> ActiveQuestTakersValue::Calculate()
|
||||
|
||||
for (auto& guidp : entry.second)
|
||||
{
|
||||
if (guidp.isDead())
|
||||
if (!guidp.IsCreatureOrGOAccessible())
|
||||
continue;
|
||||
|
||||
retQuestTakers.push_back(guidp);
|
||||
@@ -298,7 +298,7 @@ std::vector<GuidPosition> ActiveQuestObjectivesValue::Calculate()
|
||||
{
|
||||
for (auto& guidp : entry.second)
|
||||
{
|
||||
if (guidp.isDead())
|
||||
if (!guidp.IsCreatureOrGOAccessible())
|
||||
continue;
|
||||
|
||||
retQuestObjectives.push_back(guidp);
|
||||
|
||||
@@ -216,6 +216,7 @@ public:
|
||||
creators["formation"] = &ValueContext::formation;
|
||||
creators["stance"] = &ValueContext::stance;
|
||||
creators["item usage"] = &ValueContext::item_usage;
|
||||
creators["item upgrade"] = &ValueContext::item_upgrade;
|
||||
creators["speed"] = &ValueContext::speed;
|
||||
creators["last said"] = &ValueContext::last_said;
|
||||
creators["last emote"] = &ValueContext::last_emote;
|
||||
@@ -341,6 +342,7 @@ private:
|
||||
static UntypedValue* already_seen_players(PlayerbotAI* botAI) { return new AlreadySeenPlayersValue(botAI); }
|
||||
static UntypedValue* new_player_nearby(PlayerbotAI* botAI) { return new NewPlayerNearbyValue(botAI); }
|
||||
static UntypedValue* item_usage(PlayerbotAI* botAI) { return new ItemUsageValue(botAI); }
|
||||
static UntypedValue* item_upgrade(PlayerbotAI* botAI) { return new ItemUpgradeValue(botAI); }
|
||||
static UntypedValue* formation(PlayerbotAI* botAI) { return new FormationValue(botAI); }
|
||||
static UntypedValue* stance(PlayerbotAI* botAI) { return new StanceValue(botAI); }
|
||||
static UntypedValue* mana_save_level(PlayerbotAI* botAI) { return new ManaSaveLevelValue(botAI); }
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
creators["questgiver quest details"] = &WorldPacketTriggerContext::questgiver_quest_details;
|
||||
|
||||
creators["item push result"] = &WorldPacketTriggerContext::item_push_result;
|
||||
creators["loot roll won"] = &WorldPacketTriggerContext::loot_roll_won;
|
||||
creators["party command"] = &WorldPacketTriggerContext::party_command;
|
||||
creators["taxi done"] = &WorldPacketTriggerContext::taxi_done;
|
||||
creators["cast failed"] = &WorldPacketTriggerContext::cast_failed;
|
||||
@@ -92,6 +93,7 @@ private:
|
||||
static Trigger* taxi_done(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "taxi done"); }
|
||||
static Trigger* party_command(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "party command"); }
|
||||
static Trigger* item_push_result(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "item push result"); }
|
||||
static Trigger* loot_roll_won(PlayerbotAI* botAI) { return new WorldPacketTrigger(botAI, "loot roll won"); }
|
||||
|
||||
// quest
|
||||
static Trigger* quest_update_add_kill(PlayerbotAI* ai) { return new WorldPacketTrigger(ai, "quest update add kill"); }
|
||||
|
||||
@@ -41,6 +41,18 @@ public:
|
||||
std::string const GetTargetName() override { return "pet target"; }
|
||||
};
|
||||
|
||||
class CastUnendingBreathAction : public CastBuffSpellAction
|
||||
{
|
||||
public:
|
||||
CastUnendingBreathAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "unending breath") {}
|
||||
};
|
||||
|
||||
class CastUnendingBreathOnPartyAction : public BuffOnPartyAction
|
||||
{
|
||||
public:
|
||||
CastUnendingBreathOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "unending breath") {}
|
||||
};
|
||||
|
||||
class CreateSoulShardAction : public Action
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -85,6 +85,8 @@ void GenericWarlockNonCombatStrategy::InitTriggers(std::vector<TriggerNode*>& tr
|
||||
triggers.push_back(new TriggerNode("too many soul shards", { NextAction("destroy soul shard", 60.0f) }));
|
||||
triggers.push_back(new TriggerNode("soul link", { NextAction("soul link", 28.0f) }));
|
||||
triggers.push_back(new TriggerNode("demon armor", { NextAction("fel armor", 27.0f) }));
|
||||
triggers.push_back(new TriggerNode("unending breath", { NextAction("unending breath", 12.0f) }));
|
||||
triggers.push_back(new TriggerNode("unending breath on party", { NextAction("unending breath on party", 11.0f) }));
|
||||
triggers.push_back(new TriggerNode("no healthstone", { NextAction("create healthstone", 26.0f) }));
|
||||
triggers.push_back(new TriggerNode("no soulstone", { NextAction("create soulstone", 25.0f) }));
|
||||
triggers.push_back(new TriggerNode("life tap", { NextAction("life tap", 23.0f) }));
|
||||
|
||||
@@ -79,6 +79,16 @@ bool SoulLinkTrigger::IsActive()
|
||||
return !botAI->HasAura("soul link", target);
|
||||
}
|
||||
|
||||
bool UnendingBreathTrigger::IsActive()
|
||||
{
|
||||
return BuffTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target");
|
||||
}
|
||||
|
||||
bool UnendingBreathOnPartyTrigger::IsActive()
|
||||
{
|
||||
return BuffOnPartyTrigger::IsActive() && AI_VALUE2(bool, "swimming", "self target");
|
||||
}
|
||||
|
||||
bool DemonicEmpowermentTrigger::IsActive()
|
||||
{
|
||||
Pet* pet = bot->GetPet();
|
||||
|
||||
@@ -32,6 +32,20 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class UnendingBreathTrigger : public BuffTrigger
|
||||
{
|
||||
public:
|
||||
UnendingBreathTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "unending breath", 5 * 2000) {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class UnendingBreathOnPartyTrigger : public BuffOnPartyTrigger
|
||||
{
|
||||
public:
|
||||
UnendingBreathOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "unending breath on party", 2 * 2000) {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class OutOfSoulShardsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -143,6 +143,8 @@ public:
|
||||
creators["shadow trance"] = &WarlockTriggerFactoryInternal::shadow_trance;
|
||||
creators["demon armor"] = &WarlockTriggerFactoryInternal::demon_armor;
|
||||
creators["soul link"] = &WarlockTriggerFactoryInternal::soul_link;
|
||||
creators["unending breath"] = &WarlockTriggerFactoryInternal::unending_breath;
|
||||
creators["unending breath on party"] = &WarlockTriggerFactoryInternal::unending_breath_on_party;
|
||||
creators["no soul shard"] = &WarlockTriggerFactoryInternal::no_soul_shard;
|
||||
creators["too many soul shards"] = &WarlockTriggerFactoryInternal::too_many_soul_shards;
|
||||
creators["no healthstone"] = &WarlockTriggerFactoryInternal::HasHealthstone;
|
||||
@@ -189,6 +191,8 @@ private:
|
||||
static Trigger* shadow_trance(PlayerbotAI* botAI) { return new ShadowTranceTrigger(botAI); }
|
||||
static Trigger* demon_armor(PlayerbotAI* botAI) { return new DemonArmorTrigger(botAI); }
|
||||
static Trigger* soul_link(PlayerbotAI* botAI) { return new SoulLinkTrigger(botAI); }
|
||||
static Trigger* unending_breath(PlayerbotAI* botAI) { return new UnendingBreathTrigger(botAI); }
|
||||
static Trigger* unending_breath_on_party(PlayerbotAI* botAI) { return new UnendingBreathOnPartyTrigger(botAI); }
|
||||
static Trigger* no_soul_shard(PlayerbotAI* botAI) { return new OutOfSoulShardsTrigger(botAI); }
|
||||
static Trigger* too_many_soul_shards(PlayerbotAI* botAI) { return new TooManySoulShardsTrigger(botAI); }
|
||||
static Trigger* HasHealthstone(PlayerbotAI* botAI) { return new HasHealthstoneTrigger(botAI); }
|
||||
@@ -240,6 +244,8 @@ public:
|
||||
creators["demon armor"] = &WarlockAiObjectContextInternal::demon_armor;
|
||||
creators["demon skin"] = &WarlockAiObjectContextInternal::demon_skin;
|
||||
creators["soul link"] = &WarlockAiObjectContextInternal::soul_link;
|
||||
creators["unending breath"] = &WarlockAiObjectContextInternal::unending_breath;
|
||||
creators["unending breath on party"] = &WarlockAiObjectContextInternal::unending_breath_on_party;
|
||||
creators["create soul shard"] = &WarlockAiObjectContextInternal::create_soul_shard;
|
||||
creators["destroy soul shard"] = &WarlockAiObjectContextInternal::destroy_soul_shard;
|
||||
creators["create healthstone"] = &WarlockAiObjectContextInternal::create_healthstone;
|
||||
@@ -313,6 +319,8 @@ private:
|
||||
static Action* demon_armor(PlayerbotAI* botAI) { return new CastDemonArmorAction(botAI); }
|
||||
static Action* demon_skin(PlayerbotAI* botAI) { return new CastDemonSkinAction(botAI); }
|
||||
static Action* soul_link(PlayerbotAI* botAI) { return new CastSoulLinkAction(botAI); }
|
||||
static Action* unending_breath(PlayerbotAI* botAI) { return new CastUnendingBreathAction(botAI); }
|
||||
static Action* unending_breath_on_party(PlayerbotAI* botAI) { return new CastUnendingBreathOnPartyAction(botAI); }
|
||||
static Action* create_soul_shard(PlayerbotAI* botAI) { return new CreateSoulShardAction(botAI); }
|
||||
static Action* destroy_soul_shard(PlayerbotAI* botAI) { return new DestroySoulShardAction(botAI); }
|
||||
static Action* create_healthstone(PlayerbotAI* botAI) { return new CastCreateHealthstoneAction(botAI); }
|
||||
|
||||
@@ -62,7 +62,7 @@ bool MountDrakeAction::Execute(Event event)
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<Player*> players = botAI->GetPlayersInGroup();
|
||||
std::vector<Player*> players = botAI->GetAllPlayersInGroup();
|
||||
for (Player* player : players)
|
||||
{
|
||||
if (!player || !player->IsInWorld() || player->IsDuringRemoveFromWorld())
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "RaidGruulsLairHelpers.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "Unit.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
@@ -12,6 +13,8 @@ using namespace GruulsLairHelpers;
|
||||
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
if (!maulgar)
|
||||
return false;
|
||||
|
||||
MarkTargetWithSquare(bot, maulgar);
|
||||
SetRtiTarget(botAI, "square", maulgar);
|
||||
@@ -21,31 +24,20 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
|
||||
if (maulgar->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition;
|
||||
const Position& position = MAULGAR_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||
}
|
||||
|
||||
float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(),
|
||||
maulgar->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(maulgar))
|
||||
{
|
||||
return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(),
|
||||
maulgar->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -55,6 +47,8 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
{
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
if (!olm)
|
||||
return false;
|
||||
|
||||
MarkTargetWithCircle(bot, olm);
|
||||
SetRtiTarget(botAI, "circle", olm);
|
||||
@@ -64,29 +58,22 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
|
||||
if (olm->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::OlmTankPosition;
|
||||
const Position& position = OLM_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
const float olmTankLeeway = 30.0f;
|
||||
|
||||
float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float distanceOlmToPosition = olm->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceOlmToTankPosition > olmTankLeeway)
|
||||
if (distanceOlmToPosition > olmTankLeeway)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float moveX = bot->GetPositionX() + (dX / distanceOlmToPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceOlmToPosition) * maxDistance;
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(olm))
|
||||
{
|
||||
return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(),
|
||||
olm->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -95,6 +82,8 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
{
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
if (!blindeye)
|
||||
return false;
|
||||
|
||||
MarkTargetWithStar(bot, blindeye);
|
||||
SetRtiTarget(botAI, "star", blindeye);
|
||||
@@ -104,31 +93,20 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
|
||||
if (blindeye->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition;
|
||||
const Position& position = BLINDEYE_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(),
|
||||
blindeye->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(blindeye))
|
||||
{
|
||||
return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(),
|
||||
blindeye->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -138,6 +116,8 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
if (!krosh)
|
||||
return false;
|
||||
|
||||
MarkTargetWithTriangle(bot, krosh);
|
||||
SetRtiTarget(botAI, "triangle", krosh);
|
||||
@@ -149,25 +129,22 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
return botAI->CastSpell("fire ward", bot);
|
||||
|
||||
if (bot->GetTarget() != krosh->GetGUID())
|
||||
{
|
||||
bot->SetSelection(krosh->GetGUID());
|
||||
return true;
|
||||
}
|
||||
return Attack(krosh);
|
||||
|
||||
if (krosh->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::KroshTankPosition;
|
||||
float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
const Position& position = KROSH_TANK_POSITION;
|
||||
float distanceToKrosh = krosh->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
const float minDistance = 16.0f;
|
||||
const float maxDistance = 29.0f;
|
||||
const float tankPositionLeeway = 1.0f;
|
||||
|
||||
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
||||
{
|
||||
if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway))
|
||||
if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), tankPositionLeeway))
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false,
|
||||
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
||||
@@ -179,7 +156,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -192,20 +169,19 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
||||
{
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
if (!kiggler)
|
||||
return false;
|
||||
|
||||
MarkTargetWithDiamond(bot, kiggler);
|
||||
SetRtiTarget(botAI, "diamond", kiggler);
|
||||
|
||||
if (bot->GetTarget() != kiggler->GetGUID())
|
||||
{
|
||||
bot->SetSelection(kiggler->GetGUID());
|
||||
return true;
|
||||
}
|
||||
return Attack(kiggler);
|
||||
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -216,120 +192,105 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
||||
{
|
||||
// Target priority 1: Blindeye
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
if (blindeye && blindeye->IsAlive())
|
||||
if (blindeye)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "star", blindeye);
|
||||
|
||||
if (bot->GetTarget() != blindeye->GetGUID())
|
||||
{
|
||||
bot->SetSelection(blindeye->GetGUID());
|
||||
return Attack(blindeye);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 2: Olm
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
if (olm && olm->IsAlive())
|
||||
if (olm)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "circle", olm);
|
||||
|
||||
if (bot->GetTarget() != olm->GetGUID())
|
||||
{
|
||||
bot->SetSelection(olm->GetGUID());
|
||||
return Attack(olm);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 3a: Krosh (ranged only)
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
if (krosh && krosh->IsAlive() && botAI->IsRanged(bot))
|
||||
if (krosh && botAI->IsRanged(bot))
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "triangle", krosh);
|
||||
|
||||
if (bot->GetTarget() != krosh->GetGUID())
|
||||
{
|
||||
bot->SetSelection(krosh->GetGUID());
|
||||
return Attack(krosh);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 3b: Kiggler
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
if (kiggler && kiggler->IsAlive())
|
||||
if (kiggler)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "diamond", kiggler);
|
||||
|
||||
if (bot->GetTarget() != kiggler->GetGUID())
|
||||
{
|
||||
bot->SetSelection(kiggler->GetGUID());
|
||||
return Attack(kiggler);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 4: Maulgar
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
if (maulgar && maulgar->IsAlive())
|
||||
if (maulgar)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "square", maulgar);
|
||||
|
||||
if (bot->GetTarget() != maulgar->GetGUID())
|
||||
{
|
||||
bot->SetSelection(maulgar->GetGUID());
|
||||
return Attack(maulgar);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -338,22 +299,22 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
||||
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
||||
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
{
|
||||
const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter;
|
||||
const float maxDistanceFromFight = 50.0f;
|
||||
float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y);
|
||||
const Position& center = MAULGAR_ROOM_CENTER;
|
||||
const float maxDistanceFromCenter = 50.0f;
|
||||
float distToCenter = bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY());
|
||||
|
||||
if (distToFight > maxDistanceFromFight)
|
||||
if (distToCenter > maxDistanceFromCenter)
|
||||
{
|
||||
float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x);
|
||||
float destX = fightCenter.x + 40.0f * cos(angle);
|
||||
float destY = fightCenter.y + 40.0f * sin(angle);
|
||||
float destZ = fightCenter.z;
|
||||
float angle = atan2(bot->GetPositionY() - center.GetPositionY(), bot->GetPositionX() - center.GetPositionX());
|
||||
float destX = center.GetPositionX() + 40.0f * cos(angle);
|
||||
float destY = center.GetPositionY() + 40.0f * sin(angle);
|
||||
float destZ = center.GetPositionZ();
|
||||
|
||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||
bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -362,7 +323,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -373,6 +334,8 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
if (!maulgar)
|
||||
return false;
|
||||
|
||||
const float safeDistance = 10.0f;
|
||||
float distance = bot->GetExactDist2d(maulgar);
|
||||
@@ -395,7 +358,7 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -439,7 +402,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event)
|
||||
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
||||
{
|
||||
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
||||
if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true))
|
||||
if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker))
|
||||
return botAI->CastSpell("banish", assignedFelStalker);
|
||||
}
|
||||
|
||||
@@ -528,40 +491,33 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event)
|
||||
// Gruul the Dragonkiller Actions
|
||||
|
||||
// Position in center of the room
|
||||
bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event)
|
||||
bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event event)
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (!gruul)
|
||||
return false;
|
||||
|
||||
if (bot->GetVictim() != gruul)
|
||||
return Attack(gruul);
|
||||
|
||||
if (gruul->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
const Position& position = GRUUL_TANK_POSITION;
|
||||
const float maxDistance = 5.0f;
|
||||
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float distanceToTankPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
{
|
||||
float step = std::min(maxDistance, distanceToTankPosition);
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
||||
const float moveZ = tankPosition.z;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false,
|
||||
const float moveZ = position.GetPositionZ();
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, moveZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(),
|
||||
gruul->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(gruul))
|
||||
{
|
||||
return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -579,16 +535,16 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
||||
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||
if (gruul && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||
{
|
||||
initialPositions.clear();
|
||||
hasReachedInitialPosition.clear();
|
||||
initialPositions.erase(bot->GetGUID());
|
||||
hasReachedInitialPosition.erase(bot->GetGUID());
|
||||
}
|
||||
|
||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||
const float centerX = tankPosition.x;
|
||||
const float centerY = tankPosition.y;
|
||||
float centerZ = bot->GetPositionZ();
|
||||
const Position& position = GRUUL_TANK_POSITION;
|
||||
const float centerX = position.GetPositionX();
|
||||
const float centerY = position.GetPositionY();
|
||||
const float centerZ = position.GetPositionZ();
|
||||
const float minRadius = 25.0f;
|
||||
const float maxRadius = 40.0f;
|
||||
|
||||
@@ -642,7 +598,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
||||
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,10 +85,10 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction
|
||||
class GruulTheDragonkillerTanksPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank position boss") : AttackAction(botAI, name) {};
|
||||
GruulTheDragonkillerTanksPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller tanks position boss") : AttackAction(botAI, name) {};
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
@@ -8,18 +8,11 @@
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
|
||||
static bool IsChargeAction(Action* action)
|
||||
{
|
||||
return dynamic_cast<CastChargeAction*>(action) ||
|
||||
dynamic_cast<CastInterceptAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeBearAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeCatAction*>(action);
|
||||
}
|
||||
|
||||
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
|
||||
@@ -38,12 +31,10 @@ float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
(!kiggler || !kiggler->IsAlive()) &&
|
||||
(!krosh || !krosh->IsAlive()) &&
|
||||
(!olm || !olm->IsAlive()) &&
|
||||
(!blindeye || !blindeye->IsAlive()))
|
||||
!kiggler && !krosh && !olm && !blindeye)
|
||||
{
|
||||
if (IsChargeAction(action) || (dynamic_cast<MovementAction*>(action) &&
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
(dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -57,7 +48,8 @@ float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
|
||||
if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast<CastArcaneShotAction*>(action))
|
||||
if (krosh && target && target->GetGUID() == krosh->GetGUID() &&
|
||||
dynamic_cast<CastArcaneShotAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
@@ -101,8 +93,9 @@ float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action)
|
||||
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2))
|
||||
{
|
||||
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||
IsChargeAction(action))
|
||||
if ((dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss;
|
||||
creators["gruul the dragonkiller tanks position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_tanks_position_boss;
|
||||
creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged;
|
||||
creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(botAI); }
|
||||
static Action* gruul_the_dragonkiller_tanks_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerTanksPositionBossAction(botAI); }
|
||||
static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); }
|
||||
static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); }
|
||||
};
|
||||
|
||||
@@ -22,8 +22,8 @@ public:
|
||||
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank;
|
||||
creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range;
|
||||
creators["gruul the dragonkiller boss engaged by tanks"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_tanks;
|
||||
creators["gruul the dragonkiller boss engaged by ranged"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_ranged;
|
||||
creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ private:
|
||||
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_tanks(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByTanksTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangedTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); }
|
||||
};
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ void RaidGruulsLairStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) }));
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", {
|
||||
NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1) }));
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by tanks", {
|
||||
NextAction("gruul the dragonkiller tanks position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", {
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by ranged", {
|
||||
NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", {
|
||||
|
||||
@@ -10,35 +10,35 @@ bool HighKingMaulgarIsMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive();
|
||||
return botAI->IsMainTank(bot) && maulgar;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
|
||||
return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive();
|
||||
return botAI->IsAssistTankOfIndex(bot, 0, false) && olm;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive();
|
||||
return botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive();
|
||||
return IsKroshMageTank(botAI, bot) && krosh;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
||||
{
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
|
||||
return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive();
|
||||
return IsKigglerMoonkinTank(botAI, bot) && kiggler;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||
@@ -50,11 +50,11 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
||||
!(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) &&
|
||||
!(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) &&
|
||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive());
|
||||
!(botAI->IsMainTank(bot) && maulgar) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) &&
|
||||
!(IsKroshMageTank(botAI, bot) && krosh) &&
|
||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler);
|
||||
}
|
||||
|
||||
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
||||
@@ -66,7 +66,7 @@ bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
return maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
!botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive()
|
||||
{
|
||||
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
||||
|
||||
return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK;
|
||||
return felStalker && bot->getClass() == CLASS_WARLOCK;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
@@ -120,12 +120,12 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f &&
|
||||
olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank);
|
||||
return olm && olm->GetHealthPct() > 98.0f &&
|
||||
olmTank && botAI->CanCastSpell("misdirection", olmTank);
|
||||
|
||||
case 1:
|
||||
return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f &&
|
||||
blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||
return blindeye && blindeye->GetHealthPct() > 90.0f &&
|
||||
blindeyeTank && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||
|
||||
default:
|
||||
break;
|
||||
@@ -136,25 +136,24 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
|
||||
// Gruul the Dragonkiller Triggers
|
||||
|
||||
bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive()
|
||||
bool GruulTheDragonkillerBossEngagedByTanksTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() && botAI->IsMainTank(bot);
|
||||
return gruul && botAI->IsTank(bot);
|
||||
}
|
||||
|
||||
bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive()
|
||||
bool GruulTheDragonkillerBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() && botAI->IsRanged(bot);
|
||||
return gruul && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() &&
|
||||
(bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||
return gruul && (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||
}
|
||||
|
||||
@@ -73,17 +73,17 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger
|
||||
class GruulTheDragonkillerBossEngagedByTanksTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {}
|
||||
GruulTheDragonkillerBossEngagedByTanksTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by tanks") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger
|
||||
class GruulTheDragonkillerBossEngagedByRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {}
|
||||
GruulTheDragonkillerBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,19 +6,16 @@
|
||||
|
||||
namespace GruulsLairHelpers
|
||||
{
|
||||
namespace GruulsLairLocations
|
||||
{
|
||||
// Olm does not chase properly due to the Core's caster movement issues
|
||||
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
||||
// It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location
|
||||
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
||||
const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f };
|
||||
const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f };
|
||||
const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f };
|
||||
const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f };
|
||||
const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f };
|
||||
const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f };
|
||||
}
|
||||
// Olm does not chase properly due to the Core's caster movement issues
|
||||
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
||||
// It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location
|
||||
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
||||
const Position MAULGAR_TANK_POSITION = { 90.686f, 167.047f, -13.234f };
|
||||
const Position OLM_TANK_POSITION = { 87.485f, 234.942f, -3.635f };
|
||||
const Position BLINDEYE_TANK_POSITION = { 99.681f, 213.989f, -10.345f };
|
||||
const Position KROSH_TANK_POSITION = { 116.880f, 166.208f, -14.231f };
|
||||
const Position MAULGAR_ROOM_CENTER = { 88.754f, 150.759f, -11.569f };
|
||||
const Position GRUUL_TANK_POSITION = { 241.238f, 365.025f, -4.220f };
|
||||
|
||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
||||
{
|
||||
@@ -42,84 +39,43 @@ namespace GruulsLairHelpers
|
||||
return false;
|
||||
}
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!target || !group)
|
||||
return;
|
||||
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
{
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* highestHpMage = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
// (1) First loop: Return the first assistant Mage (real player or bot)
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE)
|
||||
continue;
|
||||
|
||||
if (member->getClass() == CLASS_MAGE)
|
||||
if (group->IsAssistant(member->GetGUID()))
|
||||
return member == bot;
|
||||
}
|
||||
|
||||
// (2) Fall back to bot Mage with highest HP
|
||||
Player* highestHpMage = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
member->getClass() != CLASS_MAGE)
|
||||
continue;
|
||||
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMage || hp > highestHp)
|
||||
{
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMage || hp > highestHp)
|
||||
{
|
||||
highestHpMage = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
highestHpMage = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
|
||||
// (3) Return the found Mage tank, or nullptr if none found
|
||||
return highestHpMage == bot;
|
||||
}
|
||||
|
||||
@@ -129,30 +85,37 @@ namespace GruulsLairHelpers
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* highestHpMoonkin = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
|
||||
// (1) First loop: Return the first assistant Moonkin (real player or bot)
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID)
|
||||
continue;
|
||||
|
||||
if (member->getClass() == CLASS_DRUID)
|
||||
if (group->IsAssistant(member->GetGUID()) &&
|
||||
AiFactory::GetPlayerSpecTab(member) == DRUID_TAB_BALANCE)
|
||||
return member == bot;
|
||||
}
|
||||
|
||||
// (2) Fall back to bot Moonkin with highest HP
|
||||
Player* highestHpMoonkin = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID ||
|
||||
!GET_PLAYERBOT_AI(member) || AiFactory::GetPlayerSpecTab(member) != DRUID_TAB_BALANCE)
|
||||
continue;
|
||||
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMoonkin || hp > highestHp)
|
||||
{
|
||||
int tab = AiFactory::GetPlayerSpecTab(member);
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
{
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMoonkin || hp > highestHp)
|
||||
{
|
||||
highestHpMoonkin = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
highestHpMoonkin = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
|
||||
// (3) Return the found Moonkin tank, or nullptr if none found
|
||||
return highestHpMoonkin == bot;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,19 @@
|
||||
#define RAID_GRUULSLAIRHELPERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace GruulsLairHelpers
|
||||
{
|
||||
enum GruulsLairSpells
|
||||
{
|
||||
// High King Maulgar
|
||||
SPELL_WHIRLWIND = 33238,
|
||||
SPELL_WHIRLWIND = 33238,
|
||||
|
||||
// Krosh Firehand
|
||||
SPELL_SPELL_SHIELD = 33054,
|
||||
SPELL_SPELL_SHIELD = 33054,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Warlock
|
||||
SPELL_BANISH = 18647, // Rank 2
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
SPELL_GROUND_SLAM_1 = 33525,
|
||||
@@ -30,33 +26,20 @@ namespace GruulsLairHelpers
|
||||
NPC_WILD_FEL_STALKER = 18847,
|
||||
};
|
||||
|
||||
constexpr uint32 GRUULS_LAIR_MAP_ID = 565;
|
||||
|
||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
||||
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
||||
|
||||
struct Location
|
||||
{
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
namespace GruulsLairLocations
|
||||
{
|
||||
extern const Location MaulgarTankPosition;
|
||||
extern const Location OlmTankPosition;
|
||||
extern const Location BlindeyeTankPosition;
|
||||
extern const Location KroshTankPosition;
|
||||
extern const Location MaulgarRoomCenter;
|
||||
extern const Location GruulTankPosition;
|
||||
}
|
||||
extern const Position MAULGAR_TANK_POSITION;
|
||||
extern const Position OLM_TANK_POSITION;
|
||||
extern const Position BLINDEYE_TANK_POSITION;
|
||||
extern const Position KROSH_TANK_POSITION;
|
||||
extern const Position MAULGAR_ROOM_CENTER;
|
||||
extern const Position GRUUL_TANK_POSITION;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace KarazhanHelpers;
|
||||
|
||||
@@ -44,7 +45,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||
if (attumenMounted)
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithStar(bot, attumenMounted);
|
||||
|
||||
SetRtiTarget(botAI, "star", attumenMounted);
|
||||
@@ -57,7 +58,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
||||
}
|
||||
else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithStar(bot, midnight);
|
||||
|
||||
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||
@@ -180,7 +181,7 @@ bool MoroesMarkTargetAction::Execute(Event event)
|
||||
|
||||
if (target)
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithSkull(bot, target);
|
||||
|
||||
SetRtiTarget(botAI, "skull", target);
|
||||
@@ -405,7 +406,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event)
|
||||
if (!flare)
|
||||
return false;
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithSkull(bot, flare);
|
||||
|
||||
SetRtiTarget(botAI, "skull", flare);
|
||||
@@ -469,11 +470,11 @@ bool TheCuratorSpreadRangedAction::Execute(Event event)
|
||||
// Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof
|
||||
bool TerestianIllhoofMarkTargetAction::Execute(Event event)
|
||||
{
|
||||
Unit* demonChains = AI_VALUE2(Unit*, "find target", "demon chains");
|
||||
Unit* kilrek = AI_VALUE2(Unit*, "find target", "kil'rek");
|
||||
Unit* demonChains = GetFirstAliveUnitByEntry(botAI, NPC_DEMON_CHAINS);
|
||||
Unit* kilrek = GetFirstAliveUnitByEntry(botAI, NPC_KILREK);
|
||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
||||
|
||||
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
||||
if (target)
|
||||
MarkTargetWithSkull(bot, target);
|
||||
|
||||
@@ -1007,7 +1008,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
||||
if (netherspite->GetHealth() == netherspite->GetMaxHealth() &&
|
||||
!netherspite->HasAura(SPELL_GREEN_BEAM_HEAL))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
netherspiteDpsWaitTimer.insert_or_assign(instanceId, now);
|
||||
|
||||
if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||
@@ -1018,7 +1019,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
||||
}
|
||||
else if (netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
netherspiteDpsWaitTimer.erase(instanceId);
|
||||
|
||||
if (botAI->IsTank(bot))
|
||||
@@ -1029,7 +1030,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
||||
}
|
||||
else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
netherspiteDpsWaitTimer.try_emplace(instanceId, now);
|
||||
|
||||
if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||
@@ -1458,7 +1459,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
||||
if (botAI->IsRanged(bot))
|
||||
nightbaneRangedStep.erase(botGuid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
nightbaneDpsWaitTimer.erase(instanceId);
|
||||
}
|
||||
// Erase flight phase timer and Rain of Bones tracker on ground phase and start DPS wait timer
|
||||
@@ -1466,7 +1467,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
||||
{
|
||||
nightbaneRainOfBonesHit.erase(botGuid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
{
|
||||
nightbaneFlightPhaseStartTimer.erase(instanceId);
|
||||
nightbaneDpsWaitTimer.try_emplace(instanceId, now);
|
||||
@@ -1482,7 +1483,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
||||
if (botAI->IsRanged(bot))
|
||||
nightbaneRangedStep.erase(botGuid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
{
|
||||
nightbaneDpsWaitTimer.erase(instanceId);
|
||||
nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "MageActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PriestActions.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ShamanActions.h"
|
||||
@@ -242,6 +243,9 @@ float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action)
|
||||
|
||||
if (bot->HasAura(SPELL_ENFEEBLE))
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace KarazhanHelpers;
|
||||
|
||||
@@ -40,7 +41,7 @@ bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive()
|
||||
|
||||
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||
@@ -110,7 +111,7 @@ bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive()
|
||||
|
||||
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
||||
@@ -126,7 +127,7 @@ bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||
|
||||
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
||||
@@ -178,7 +179,7 @@ bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive()
|
||||
|
||||
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||
@@ -202,7 +203,7 @@ bool ShadeOfAranFlameWreathIsActiveTrigger::IsActive()
|
||||
// Exclusion of Banish is so the player may Banish elementals if they wish
|
||||
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
||||
@@ -279,7 +280,7 @@ bool NetherspiteBossIsBanishedTrigger::IsActive()
|
||||
|
||||
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot))
|
||||
if (!botAI->IsTank(bot) && !IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace KarazhanHelpers
|
||||
{
|
||||
@@ -52,75 +51,6 @@ namespace KarazhanHelpers
|
||||
const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
||||
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Only one bot is needed to set/reset instance-wide timers
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
||||
{
|
||||
for (Unit* unit : units)
|
||||
@@ -132,44 +62,6 @@ namespace KarazhanHelpers
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
|
||||
{
|
||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||
return unit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||
{
|
||||
Unit* nearestPlayer = nullptr;
|
||||
float nearestDistance = radius;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == bot)
|
||||
continue;
|
||||
|
||||
float distance = bot->GetExactDist2d(member);
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
nearestDistance = distance;
|
||||
nearestPlayer = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
|
||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
||||
|
||||
@@ -61,6 +61,11 @@ namespace KarazhanHelpers
|
||||
NPC_ATTUMEN_THE_HUNTSMAN = 15550,
|
||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||
|
||||
// Terestian Illhoof
|
||||
NPC_TERESTIAN_ILLHOOF = 15688,
|
||||
NPC_DEMON_CHAINS = 17248,
|
||||
NPC_KILREK = 17229,
|
||||
|
||||
// Shade of Aran
|
||||
NPC_CONJURED_ELEMENTAL = 17167,
|
||||
|
||||
@@ -74,8 +79,8 @@ namespace KarazhanHelpers
|
||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||
};
|
||||
|
||||
const uint32 KARAZHAN_MAP_ID = 532;
|
||||
const float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||
constexpr uint32 KARAZHAN_MAP_ID = 532;
|
||||
constexpr float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||
|
||||
// Attumen the Huntsman
|
||||
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||
@@ -105,17 +110,7 @@ namespace KarazhanHelpers
|
||||
extern const Position NIGHTBANE_FLIGHT_STACK_POSITION;
|
||||
extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION;
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry);
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
@@ -14,46 +15,45 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
||||
return false;
|
||||
|
||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
if (channelerSquare)
|
||||
MarkTargetWithSquare(bot, channelerSquare);
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
if (channelerStar && channelerStar->IsAlive())
|
||||
if (channelerStar)
|
||||
MarkTargetWithStar(bot, channelerStar);
|
||||
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
if (channelerCircle && channelerCircle->IsAlive())
|
||||
if (channelerCircle)
|
||||
MarkTargetWithCircle(bot, channelerCircle);
|
||||
|
||||
// After first three channelers are dead, wait for Magtheridon to activate
|
||||
if ((!channelerSquare || !channelerSquare->IsAlive()) &&
|
||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
||||
(!channelerCircle || !channelerCircle->IsAlive()))
|
||||
if (!channelerSquare && !channelerStar && !channelerCircle)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition;
|
||||
if (!bot->IsWithinDist2d(position.x, position.y, 2.0f))
|
||||
const Position& position = WAITING_FOR_MAGTHERIDON_POSITION;
|
||||
if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), 2.0f))
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, position.GetPositionX(), position.GetPositionY(),
|
||||
position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
bot->SetFacingTo(position.orientation);
|
||||
bot->SetFacingTo(position.GetOrientation());
|
||||
return true;
|
||||
}
|
||||
|
||||
Creature* currentTarget = nullptr;
|
||||
std::string rtiName;
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
if (channelerSquare)
|
||||
{
|
||||
currentTarget = channelerSquare;
|
||||
rtiName = "square";
|
||||
}
|
||||
else if (channelerStar && channelerStar->IsAlive())
|
||||
else if (channelerStar)
|
||||
{
|
||||
currentTarget = channelerStar;
|
||||
rtiName = "star";
|
||||
}
|
||||
else if (channelerCircle && channelerCircle->IsAlive())
|
||||
else if (channelerCircle)
|
||||
{
|
||||
currentTarget = channelerCircle;
|
||||
rtiName = "circle";
|
||||
@@ -70,7 +70,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
||||
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
{
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
if (!channelerDiamond || !channelerDiamond->IsAlive())
|
||||
if (!channelerDiamond)
|
||||
return false;
|
||||
|
||||
MarkTargetWithDiamond(bot, channelerDiamond);
|
||||
@@ -81,18 +81,18 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
|
||||
if (channelerDiamond->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition;
|
||||
const Position& position = NW_CHANNELER_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||
{
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
if (!channelerTriangle || !channelerTriangle->IsAlive())
|
||||
if (!channelerTriangle)
|
||||
return false;
|
||||
|
||||
MarkTargetWithTriangle(bot, channelerTriangle);
|
||||
@@ -114,18 +114,18 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||
|
||||
if (channelerTriangle->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition;
|
||||
const Position& position = NE_CHANNELER_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
if (mainTank && channelerStar && channelerStar->IsAlive() &&
|
||||
if (mainTank && channelerStar &&
|
||||
channelerStar->GetVictim() != mainTank)
|
||||
{
|
||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||
@@ -190,7 +190,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (mainTank && channelerCircle && channelerCircle->IsAlive() &&
|
||||
if (mainTank && channelerCircle &&
|
||||
channelerCircle->GetVictim() != mainTank)
|
||||
{
|
||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||
@@ -215,90 +215,69 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event)
|
||||
{
|
||||
// Listed in order of priority
|
||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
if (channelerSquare)
|
||||
{
|
||||
SetRtiTarget(botAI, "square", channelerSquare);
|
||||
|
||||
if (bot->GetTarget() != channelerSquare->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerSquare->GetGUID());
|
||||
return Attack(channelerSquare);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
if (channelerStar && channelerStar->IsAlive())
|
||||
if (channelerStar)
|
||||
{
|
||||
SetRtiTarget(botAI, "star", channelerStar);
|
||||
|
||||
if (bot->GetTarget() != channelerStar->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerStar->GetGUID());
|
||||
return Attack(channelerStar);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
if (channelerCircle && channelerCircle->IsAlive())
|
||||
if (channelerCircle)
|
||||
{
|
||||
SetRtiTarget(botAI, "circle", channelerCircle);
|
||||
|
||||
if (bot->GetTarget() != channelerCircle->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerCircle->GetGUID());
|
||||
return Attack(channelerCircle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
if (channelerDiamond && channelerDiamond->IsAlive())
|
||||
if (channelerDiamond)
|
||||
{
|
||||
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
||||
|
||||
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerDiamond->GetGUID());
|
||||
return Attack(channelerDiamond);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
if (channelerTriangle && channelerTriangle->IsAlive())
|
||||
if (channelerTriangle)
|
||||
{
|
||||
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
||||
|
||||
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerTriangle->GetGUID());
|
||||
return Attack(channelerTriangle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
||||
(!channelerSquare || !channelerSquare->IsAlive()) &&
|
||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
||||
(!channelerCircle || !channelerCircle->IsAlive()) &&
|
||||
(!channelerDiamond || !channelerDiamond->IsAlive()) &&
|
||||
(!channelerTriangle || !channelerTriangle->IsAlive()))
|
||||
!channelerSquare && !channelerStar && !channelerCircle &&
|
||||
!channelerDiamond && !channelerTriangle)
|
||||
{
|
||||
SetRtiTarget(botAI, "cross", magtheridon);
|
||||
|
||||
if (bot->GetTarget() != magtheridon->GetGUID())
|
||||
{
|
||||
bot->SetSelection(magtheridon->GetGUID());
|
||||
return Attack(magtheridon);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -343,15 +322,15 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event)
|
||||
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
||||
{
|
||||
Unit* assignedAbyssal = abyssals[warlockIndex];
|
||||
if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true))
|
||||
if (!botAI->HasAura("banish", assignedAbyssal) && botAI->CanCastSpell("banish", assignedAbyssal))
|
||||
return botAI->CastSpell("banish", assignedAbyssal);
|
||||
}
|
||||
|
||||
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
||||
{
|
||||
Unit* excessAbyssal = abyssals[i];
|
||||
if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) &&
|
||||
botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true))
|
||||
if (!botAI->HasAura("banish", excessAbyssal) && !botAI->HasAura("fear", excessAbyssal) &&
|
||||
botAI->CanCastSpell("fear", excessAbyssal))
|
||||
return botAI->CastSpell("fear", excessAbyssal);
|
||||
}
|
||||
|
||||
@@ -373,22 +352,20 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event)
|
||||
|
||||
if (magtheridon->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition;
|
||||
const Position& position = MAGTHERIDON_TANK_POSITION;
|
||||
const float maxDistance = 2.0f;
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||
}
|
||||
|
||||
bot->SetFacingTo(position.orientation);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -440,13 +417,13 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
}
|
||||
|
||||
bool isHealer = botAI->IsHeal(bot);
|
||||
const Location& center = isHealer
|
||||
? MagtheridonsLairLocations::HealerSpreadPosition
|
||||
: MagtheridonsLairLocations::RangedSpreadPosition;
|
||||
const Position& center = isHealer
|
||||
? HEALER_SPREAD_POSITION
|
||||
: RANGED_SPREAD_POSITION;
|
||||
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
||||
float centerX = center.x;
|
||||
float centerY = center.y;
|
||||
float centerZ = bot->GetPositionZ();
|
||||
float centerX = center.GetPositionX();
|
||||
float centerY = center.GetPositionY();
|
||||
float centerZ = center.GetPositionZ();
|
||||
const float radiusBuffer = 3.0f;
|
||||
|
||||
if (!initialPositions.count(bot->GetGUID()))
|
||||
@@ -479,7 +456,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||
@@ -499,7 +476,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, centerZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -593,7 +570,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -603,7 +580,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
||||
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
||||
float fallbackZ = bot->GetPositionZ();
|
||||
|
||||
return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -638,7 +615,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu
|
||||
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||
}
|
||||
|
||||
@@ -663,14 +640,14 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
||||
bool lastBlastNova = lastBlastNovaState[instanceId];
|
||||
|
||||
if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot))
|
||||
if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||
blastNovaTimer[instanceId] = now;
|
||||
|
||||
lastBlastNovaState[instanceId] = blastNovaActive;
|
||||
|
||||
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||
{
|
||||
spreadWaitTimer.try_emplace(instanceId, now);
|
||||
blastNovaTimer.try_emplace(instanceId, now);
|
||||
@@ -679,11 +656,12 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
||||
}
|
||||
else
|
||||
{
|
||||
MagtheridonSpreadRangedAction::initialPositions.clear();
|
||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear();
|
||||
botToCubeAssignment.clear();
|
||||
ObjectGuid guid = bot->GetGUID();
|
||||
MagtheridonSpreadRangedAction::initialPositions.erase(guid);
|
||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.erase(guid);
|
||||
botToCubeAssignment.erase(guid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||
{
|
||||
spreadWaitTimer.erase(instanceId);
|
||||
blastNovaTimer.erase(instanceId);
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
@@ -85,8 +83,8 @@ public:
|
||||
private:
|
||||
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
||||
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
||||
bool HandleWaitingPhase(const CubeInfo& cubeInfo);
|
||||
bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube);
|
||||
bool HandleWaitingPhase(const MagtheridonHelpers::CubeInfo& cubeInfo);
|
||||
bool HandleCubeInteraction(const MagtheridonHelpers::CubeInfo& cubeInfo, GameObject* cube);
|
||||
};
|
||||
|
||||
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "GenericSpellActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "WarlockActions.h"
|
||||
#include "WipeAction.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
@@ -24,10 +25,10 @@ float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action)
|
||||
auto it = botToCubeAssignment.find(bot->GetGUID());
|
||||
if (it != botToCubeAssignment.end())
|
||||
{
|
||||
if (dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||
if (dynamic_cast<WipeAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
return 0.0f;
|
||||
else if (!dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,28 +42,31 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action)
|
||||
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
return 1.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
const uint8 dpsWaitSeconds = 6;
|
||||
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
||||
if (it == dpsWaitTimer.end() ||
|
||||
(time(nullptr) - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot) && (dynamic_cast<AttackAction*>(action) ||
|
||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action))))
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// No tank assist for offtanks during the channeler phase
|
||||
// So they don't try to pull channelers from each other or the main tank
|
||||
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
if (!magtheridon)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
||||
dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
@@ -18,7 +18,7 @@ bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive()
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
|
||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
||||
channelerDiamond && channelerDiamond->IsAlive();
|
||||
channelerDiamond;
|
||||
}
|
||||
|
||||
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||
@@ -27,7 +27,7 @@ bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
|
||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
||||
channelerTriangle && channelerTriangle->IsAlive();
|
||||
channelerTriangle;
|
||||
}
|
||||
|
||||
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||
@@ -38,8 +38,7 @@ bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
|
||||
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
||||
((channelerStar && channelerStar->IsAlive()) ||
|
||||
(channelerCircle && channelerCircle->IsAlive()));
|
||||
(channelerStar || channelerCircle);
|
||||
}
|
||||
|
||||
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||
@@ -51,12 +50,11 @@ bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
|
||||
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive()))
|
||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle))
|
||||
return false;
|
||||
|
||||
return (channeler && channeler->IsAlive()) || (magtheridon &&
|
||||
!magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||
return channeler || (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||
}
|
||||
|
||||
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
||||
@@ -84,10 +82,8 @@ bool MagtheridonBossEngagedByMainTankTrigger::IsActive()
|
||||
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
|
||||
return magtheridon && botAI->IsRanged(bot) &&
|
||||
!(channeler && channeler->IsAlive());
|
||||
return magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||
@@ -122,7 +118,5 @@ bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||
|
||||
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
|
||||
return magtheridon;
|
||||
return AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "Creature.h"
|
||||
#include "GameObject.h"
|
||||
#include "GroupReference.h"
|
||||
#include "Map.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
namespace MagtheridonHelpers
|
||||
{
|
||||
namespace MagtheridonsLairLocations
|
||||
{
|
||||
const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f };
|
||||
const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f };
|
||||
const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||
const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||
const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||
const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||
}
|
||||
const Position WAITING_FOR_MAGTHERIDON_POSITION = { 1.359f, 2.048f, -0.406f, 3.135f };
|
||||
const Position MAGTHERIDON_TANK_POSITION = { 22.827f, 2.105f, -0.406f, 3.135f };
|
||||
const Position NW_CHANNELER_TANK_POSITION = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||
const Position NE_CHANNELER_TANK_POSITION = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||
const Position RANGED_SPREAD_POSITION = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||
const Position HEALER_SPREAD_POSITION = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||
|
||||
// Identify channelers by their database GUIDs
|
||||
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
||||
@@ -29,63 +25,11 @@ namespace MagtheridonHelpers
|
||||
if (it == map->GetCreatureBySpawnIdStore().end())
|
||||
return nullptr;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
Creature* channeler = it->second;
|
||||
if (!channeler->IsAlive())
|
||||
return nullptr;
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!target || !group)
|
||||
return;
|
||||
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCross(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
||||
return channeler;
|
||||
}
|
||||
|
||||
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
||||
@@ -208,19 +152,4 @@ namespace MagtheridonHelpers
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "Group.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace MagtheridonHelpers
|
||||
{
|
||||
@@ -19,10 +18,6 @@ namespace MagtheridonHelpers
|
||||
SPELL_BLAST_NOVA = 30616,
|
||||
SPELL_SHADOW_GRASP = 30410,
|
||||
|
||||
// Warlock
|
||||
SPELL_BANISH = 18647,
|
||||
SPELL_FEAR = 6215,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
};
|
||||
@@ -38,6 +33,7 @@ namespace MagtheridonHelpers
|
||||
GO_BLAZE = 181832,
|
||||
};
|
||||
|
||||
constexpr uint32 MAGTHERIDON_MAP_ID = 544;
|
||||
constexpr uint32 SOUTH_CHANNELER = 90978;
|
||||
constexpr uint32 WEST_CHANNELER = 90979;
|
||||
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
||||
@@ -45,31 +41,14 @@ namespace MagtheridonHelpers
|
||||
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
||||
|
||||
Creature* GetChanneler(Player* bot, uint32 dbGuid);
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void MarkTargetWithCross(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
struct Location
|
||||
{
|
||||
float x, y, z, orientation;
|
||||
};
|
||||
|
||||
namespace MagtheridonsLairLocations
|
||||
{
|
||||
extern const Location WaitingForMagtheridonPosition;
|
||||
extern const Location MagtheridonTankPosition;
|
||||
extern const Location NWChannelerTankPosition;
|
||||
extern const Location NEChannelerTankPosition;
|
||||
extern const Location RangedSpreadPosition;
|
||||
extern const Location HealerSpreadPosition;
|
||||
}
|
||||
extern const Position WAITING_FOR_MAGTHERIDON_POSITION;
|
||||
extern const Position MAGTHERIDON_TANK_POSITION;
|
||||
extern const Position NW_CHANNELER_TANK_POSITION;
|
||||
extern const Position NE_CHANNELER_TANK_POSITION;
|
||||
extern const Position RANGED_SPREAD_POSITION;
|
||||
extern const Position HEALER_SPREAD_POSITION;
|
||||
|
||||
struct CubeInfo
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "BossAuraActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcActions.h"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcTriggers.h"
|
||||
|
||||
|
||||
142
src/Ai/Raid/RaidBossHelpers.cpp
Normal file
142
src/Ai/Raid/RaidBossHelpers.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
// Functions to mark targets with raid target icons
|
||||
// Note that these functions do not allow the player to change the icon during the encounter
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCross(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||
}
|
||||
|
||||
// For bots to set their raid target icon to the specified icon on the specified target
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the first alive DPS bot in the specified instance map, excluding any specified bot
|
||||
// Intended for purposes of storing and erasing timers and trackers in associative containers
|
||||
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member->GetMapId() != mapId ||
|
||||
!GET_PLAYERBOT_AI(member) || !botAI->IsDps(member))
|
||||
continue;
|
||||
|
||||
if (member != exclude)
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the first matching alive unit from a cell search of nearby npcs
|
||||
// More responsive than "find target," but performance cost is much higher
|
||||
// Re: using the third parameter (false by default), some units are never considered
|
||||
// to be in combat (e.g., totems)
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||
{
|
||||
if (!requireInCombat || unit->IsInCombat())
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Return the nearest alive player (human or bot) within the specified radius
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||
{
|
||||
Unit* nearestPlayer = nullptr;
|
||||
float nearestDistance = radius;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == bot)
|
||||
continue;
|
||||
|
||||
float distance = bot->GetExactDist2d(member);
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
nearestDistance = distance;
|
||||
nearestPlayer = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
21
src/Ai/Raid/RaidBossHelpers.h
Normal file
21
src/Ai/Raid/RaidBossHelpers.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _PLAYERBOT_RAIDBOSSHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDBOSSHELPERS_H_
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Unit.h"
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void MarkTargetWithCross(Player* bot, Unit* target);
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr);
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat = false);
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||
|
||||
#endif
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "RaidKarazhanStrategy.h"
|
||||
#include "RaidMagtheridonStrategy.h"
|
||||
#include "RaidGruulsLairStrategy.h"
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidEoEStrategy.h"
|
||||
#include "RaidVoAStrategy.h"
|
||||
@@ -26,10 +27,11 @@ public:
|
||||
creators["karazhan"] = &RaidStrategyContext::karazhan;
|
||||
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||
creators["gruulslair"] = &RaidStrategyContext::gruulslair;
|
||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||
creators["voa"] = &RaidStrategyContext::voa;
|
||||
creators["uld"] = &RaidStrategyContext::uld;
|
||||
creators["ulduar"] = &RaidStrategyContext::ulduar;
|
||||
creators["onyxia"] = &RaidStrategyContext::onyxia;
|
||||
creators["icc"] = &RaidStrategyContext::icc;
|
||||
}
|
||||
@@ -41,11 +43,12 @@ private:
|
||||
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
|
||||
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
||||
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
|
||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); }
|
||||
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||
static Strategy* ulduar(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
|
||||
};
|
||||
|
||||
|
||||
3128
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp
Normal file
3128
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
457
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h
Normal file
457
src/Ai/Raid/SerpentshrineCavern/Action/RaidSSCActions.h
Normal file
@@ -0,0 +1,457 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCACTIONS_H
|
||||
#define _PLAYERBOT_RAIDSSCACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
// General
|
||||
|
||||
class SerpentShrineCavernEraseTimersAndTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
SerpentShrineCavernEraseTimersAndTrackersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "serpent shrine cavern erase timers and trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Trash
|
||||
|
||||
class UnderbogColossusEscapeToxicPoolAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
UnderbogColossusEscapeToxicPoolAction(
|
||||
PlayerbotAI* botAI, std::string const name = "underbog colossus escape toxic pool") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GreyheartTidecallerMarkWaterElementalTotemAction : public Action
|
||||
{
|
||||
public:
|
||||
GreyheartTidecallerMarkWaterElementalTotemAction(
|
||||
PlayerbotAI* botAI, std::string const name = "greyheart tidecaller mark water elemental totem") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
class HydrossTheUnstablePositionFrostTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstablePositionFrostTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable position frost tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstablePositionNatureTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstablePositionNatureTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable position nature tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstablePrioritizeElementalAddsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstablePrioritizeElementalAddsAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable prioritize elemental adds") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableFrostPhaseSpreadOutAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableFrostPhaseSpreadOutAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable frost phase spread out") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableMisdirectBossToTankAction : public Action
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableMisdirectBossToTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable misdirect boss to tank") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool TryMisdirectToFrostTank(Unit* hydross, Group* group);
|
||||
bool TryMisdirectToNatureTank(Unit* hydross, Group* group);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableStopDpsUponPhaseChangeAction : public Action
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableStopDpsUponPhaseChangeAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable stop dps upon phase change") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableManageTimersAction : public Action
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableManageTimersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "hydross the unstable manage timers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
class TheLurkerBelowRunAroundBehindBossAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowRunAroundBehindBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below run around behind boss") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowPositionMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowPositionMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below position main tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowSpreadRangedInArcAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowSpreadRangedInArcAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below spread ranged in arc") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowTanksPickUpAddsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowTanksPickUpAddsAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below tanks pick up adds") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowManageSpoutTimerAction : public Action
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowManageSpoutTimerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "the lurker below manage spout timer") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
class LeotherasTheBlindTargetSpellbindersAction : public Action
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindTargetSpellbindersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind target spellbinders") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindPositionRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindPositionRangedAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind position ranged") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDemonFormTankAttackBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDemonFormTankAttackBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind demon form tank attack boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMeleeTanksDontAttackDemonFormAction : public Action
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind melee tanks don't attack demon form") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindRunAwayFromWhirlwindAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindRunAwayFromWhirlwindAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind run away from whirlwind") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMeleeDpsRunAwayFromBossAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMeleeDpsRunAwayFromBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind melee dps run away from boss") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDestroyInnerDemonAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDestroyInnerDemonAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind destroy inner demon") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool HandleFeralTankStrategy(Unit* innerDemon);
|
||||
bool HandleHealerStrategy(Unit* innerDemon);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindFinalPhaseAssignDpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind final phase assign dps priority") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMisdirectBossToDemonFormTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMisdirectBossToDemonFormTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind misdirect boss to demon form tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindManageDpsWaitTimersAction : public Action
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindManageDpsWaitTimersAction(
|
||||
PlayerbotAI* botAI, std::string const name = "leotheras the blind manage dps wait timers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
class FathomLordKarathressMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress main tank position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressFirstAssistTankPositionCaribdisAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressFirstAssistTankPositionCaribdisAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress first assist tank position caribdis") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressSecondAssistTankPositionSharkkisAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressSecondAssistTankPositionSharkkisAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress second assist tank position sharkkis") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressThirdAssistTankPositionTidalvessAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressThirdAssistTankPositionTidalvessAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress third assist tank position tidalvess") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressPositionCaribdisTankHealerAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressPositionCaribdisTankHealerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress position caribdis tank healer") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressMisdirectBossesToTanksAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressMisdirectBossesToTanksAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress misdirect bosses to tanks") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressAssignDpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressAssignDpsPriorityAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress assign dps priority") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressManageDpsTimerAction : public Action
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressManageDpsTimerAction(
|
||||
PlayerbotAI* botAI, std::string const name = "fathom-lord karathress manage dps timer") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
class MorogrimTidewalkerMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerMoveBossToTankPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerMoveBossToTankPositionAction(
|
||||
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker move boss to tank position") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool MoveToPhase1TankPosition(Unit* tidewalker);
|
||||
bool MoveToPhase2TankPosition(Unit* tidewalker);
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerPhase2RepositionRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerPhase2RepositionRangedAction(
|
||||
PlayerbotAI* botAI, std::string const name = "morogrim tidewalker phase 2 reposition ranged") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
class LadyVashjMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj main tank position boss") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjPhase1SpreadRangedInArcAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjPhase1SpreadRangedInArcAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj phase 1 spread ranged in arc") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjSetGroundingTotemInMainTankGroupAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjSetGroundingTotemInMainTankGroupAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj set grounding totem in main tank group") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjStaticChargeMoveAwayFromGroupAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjStaticChargeMoveAwayFromGroupAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj static charge move away from group") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj misdirect boss to main tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjAssignPhase2AndPhase3DpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjAssignPhase2AndPhase3DpsPriorityAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj assign phase 2 and phase 3 dps priority") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjMisdirectStriderToFirstAssistTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjMisdirectStriderToFirstAssistTankAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj misdirect strider to first assist tank") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjTankAttackAndMoveAwayStriderAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjTankAttackAndMoveAwayStriderAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj tank attack and move away strider") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjTeleportToTaintedElementalAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
LadyVashjTeleportToTaintedElementalAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj teleport to tainted elemental") : AttackAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjLootTaintedCoreAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjLootTaintedCoreAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj loot tainted core") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjPassTheTaintedCoreAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjPassTheTaintedCoreAction(
|
||||
PlayerbotAI* botAI, std::string const name = "lady vashj pass the tainted core") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool LineUpFirstCorePasser(Player* designatedLooter, Unit* closestTrigger);
|
||||
bool LineUpSecondCorePasser(Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool LineUpThirdCorePasser(Player* designatedLooter, Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool LineUpFourthCorePasser(Player* firstCorePasser, Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFirstCorePasserInIntendedPosition(Player* designatedLooter, Player* firstCorePasser, Unit* closestTrigger);
|
||||
bool IsSecondCorePasserInIntendedPosition(Player* firstCorePasser, Player* secondCorePasser, Unit* closestTrigger);
|
||||
bool IsThirdCorePasserInIntendedPosition(Player* secondCorePasser, Player* thirdCorePasser, Unit* closestTrigger);
|
||||
bool IsFourthCorePasserInIntendedPosition(Player* thirdCorePasser, Player* fourthCorePasser, Unit* closestTrigger);
|
||||
void ScheduleTransferCoreAfterImbue(PlayerbotAI* botAI, Player* giver, Player* receiver);
|
||||
bool UseCoreOnNearestGenerator(const uint32 instanceId);
|
||||
};
|
||||
|
||||
class LadyVashjDestroyTaintedCoreAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjDestroyTaintedCoreAction(PlayerbotAI* botAI, std::string const name = "lady vashj destroy tainted core") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjEraseCorePassingTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjEraseCorePassingTrackersAction(PlayerbotAI* botAI, std::string const name = "lady vashj erase core passing trackers") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class LadyVashjAvoidToxicSporesAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
LadyVashjAvoidToxicSporesAction(PlayerbotAI* botAI, std::string const name = "lady vashj avoid toxic spores") : MovementAction(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
static std::vector<Unit*> GetAllSporeDropTriggers(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
private:
|
||||
Position FindSafestNearbyPosition(const std::vector<Unit*>& spores, const Position& position, float maxRadius, float hazardRadius);
|
||||
bool IsPathSafeFromSpores(const Position& start, const Position& end, const std::vector<Unit*>& spores, float hazardRadius);
|
||||
};
|
||||
|
||||
class LadyVashjUseFreeActionAbilitiesAction : public Action
|
||||
{
|
||||
public:
|
||||
LadyVashjUseFreeActionAbilitiesAction(PlayerbotAI* botAI, std::string const name = "lady vashj use free action abilities") : Action(botAI, name) {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,799 @@
|
||||
#include "RaidSSCMultipliers.h"
|
||||
#include "RaidSSCActions.h"
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DestroyItemAction.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "DruidCatActions.h"
|
||||
#include "DruidShapeshiftActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "LootAction.h"
|
||||
#include "MageActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "WarlockActions.h"
|
||||
#include "WarriorActions.h"
|
||||
#include "WipeAction.h"
|
||||
|
||||
using namespace SerpentShrineCavernHelpers;
|
||||
|
||||
// Trash
|
||||
|
||||
float UnderbogColossusEscapeToxicPoolMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->HasAura(SPELL_TOXIC_POOL))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<UnderbogColossusEscapeToxicPoolAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
float HydrossTheUnstableDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
return 1.0f;
|
||||
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (!hydross)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
(dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<HydrossTheUnstablePositionFrostTankAction*>(action) &&
|
||||
!dynamic_cast<HydrossTheUnstablePositionNatureTankAction*>(action)))
|
||||
{
|
||||
if ((botAI->IsMainTank(bot) && hydross->HasAura(SPELL_CORRUPTION)) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0, true) && !hydross->HasAura(SPELL_CORRUPTION)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float HydrossTheUnstableWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (!hydross)
|
||||
return 1.0f;
|
||||
|
||||
Unit* waterElemental = AI_VALUE2(Unit*, "find target", "pure spawn of hydross");
|
||||
Unit* natureElemental = AI_VALUE2(Unit*, "find target", "tainted spawn of hydross");
|
||||
if (botAI->IsAssistTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||
(waterElemental || natureElemental))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<HydrossTheUnstableMisdirectBossToTankAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = hydross->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
constexpr uint8 phaseChangeWaitSeconds = 1;
|
||||
constexpr uint8 dpsWaitSeconds = 5;
|
||||
|
||||
if (!hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsMainTank(bot))
|
||||
{
|
||||
auto itDps = hydrossFrostDpsWaitTimer.find(instanceId);
|
||||
auto itPhase = hydrossChangeToFrostPhaseTimer.find(instanceId);
|
||||
|
||||
bool justChanged = (itDps == hydrossFrostDpsWaitTimer.end() ||
|
||||
(now - itDps->second) < dpsWaitSeconds);
|
||||
bool aboutToChange = (itPhase != hydrossChangeToFrostPhaseTimer.end() &&
|
||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||
|
||||
if (justChanged || aboutToChange)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (hydross->HasAura(SPELL_CORRUPTION) && !botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
{
|
||||
auto itDps = hydrossNatureDpsWaitTimer.find(instanceId);
|
||||
auto itPhase = hydrossChangeToNaturePhaseTimer.find(instanceId);
|
||||
|
||||
bool justChanged = (itDps == hydrossNatureDpsWaitTimer.end() ||
|
||||
(now - itDps->second) < dpsWaitSeconds);
|
||||
bool aboutToChange = (itPhase != hydrossChangeToNaturePhaseTimer.end() &&
|
||||
(now - itPhase->second) > phaseChangeWaitSeconds);
|
||||
|
||||
if (justChanged || aboutToChange)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float HydrossTheUnstableControlMisdirectionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||
{
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
float TheLurkerBelowStayAwayFromSpoutMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return 1.0f;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
if (it != lurkerSpoutTimer.end() && it->second > now)
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<TheLurkerBelowRunAroundBehindBossAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float TheLurkerBelowMaintainRangedSpreadMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "the lurker below"))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Disable tank assist during Submerge only if there are 3 or more tanks in the raid
|
||||
float TheLurkerBelowDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||
return 1.0f;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 1.0f;
|
||||
|
||||
uint8 tankCount = 0;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->IsTank(member))
|
||||
++tankCount;
|
||||
}
|
||||
|
||||
if (tankCount >= 3)
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
float LeotherasTheBlindAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||
if (!leotherasHuman)
|
||||
return 1.0f;
|
||||
|
||||
if (!leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
(leotherasHuman->HasAura(SPELL_WHIRLWIND) ||
|
||||
leotherasHuman->HasAura(SPELL_WHIRLWIND_CHANNEL)))
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<LeotherasTheBlindRunAwayFromWhirlwindAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot) || bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return 1.0f;
|
||||
|
||||
if (GetPhase2LeotherasDemon(botAI) && dynamic_cast<AttackAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (!GetPhase3LeotherasDemon(botAI) && dynamic_cast<CastBerserkAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindFocusOnInnerDemonMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<CastHealingSpellAction*>(action) ||
|
||||
dynamic_cast<CastCureSpellAction*>(action) ||
|
||||
dynamic_cast<CurePartyMemberAction*>(action) ||
|
||||
dynamic_cast<CastBuffSpellAction*>(action) ||
|
||||
dynamic_cast<ResurrectPartyMemberAction*>(action) ||
|
||||
dynamic_cast<PartyMemberActionNameSupport*>(action) ||
|
||||
dynamic_cast<CastBearFormAction*>(action) ||
|
||||
dynamic_cast<CastDireBearFormAction*>(action) ||
|
||||
dynamic_cast<CastTreeFormAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (!GetPhase2LeotherasDemon(botAI))
|
||||
return 1.0f;
|
||||
|
||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||
if (chaosBlast && chaosBlast->GetStackAmount() >= 5)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LeotherasTheBlindWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<LeotherasTheBlindMisdirectBossToDemonFormTankAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
const uint32 instanceId = leotheras->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase1 = 5;
|
||||
Unit* leotherasHuman = GetLeotherasHuman(botAI);
|
||||
Unit* leotherasPhase3Demon = GetPhase3LeotherasDemon(botAI);
|
||||
if (leotherasHuman && !leotherasHuman->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
!leotherasPhase3Demon)
|
||||
{
|
||||
if (botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
auto it = leotherasHumanFormDpsWaitTimer.find(instanceId);
|
||||
if (it == leotherasHumanFormDpsWaitTimer.end() ||
|
||||
(now - it->second) < dpsWaitSecondsPhase1)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase2 = 12;
|
||||
Unit* leotherasPhase2Demon = GetPhase2LeotherasDemon(botAI);
|
||||
Player* demonFormTank = GetLeotherasDemonFormTank(bot);
|
||||
if (leotherasPhase2Demon)
|
||||
{
|
||||
if (demonFormTank && demonFormTank == bot)
|
||||
return 1.0f;
|
||||
|
||||
if (!demonFormTank && botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
auto it = leotherasDemonFormDpsWaitTimer.find(instanceId);
|
||||
if (it == leotherasDemonFormDpsWaitTimer.end() ||
|
||||
(now - it->second) < dpsWaitSecondsPhase2)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint8 dpsWaitSecondsPhase3 = 8;
|
||||
if (leotherasPhase3Demon)
|
||||
{
|
||||
if ((demonFormTank && demonFormTank == bot) || botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
auto it = leotherasFinalPhaseDpsWaitTimer.find(instanceId);
|
||||
if (it == leotherasFinalPhaseDpsWaitTimer.end() ||
|
||||
(now - it->second) < dpsWaitSecondsPhase3)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Don't use Bloodlust/Heroism during the Channeler phase
|
||||
float LeotherasTheBlindDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return 1.0f;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (leotheras && leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
{
|
||||
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||
dynamic_cast<CastBloodlustAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
float FathomLordKarathressDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<AvoidAoeAction*>(action) ||
|
||||
dynamic_cast<CastTauntAction*>(action) ||
|
||||
dynamic_cast<CastChallengingShoutAction*>(action) ||
|
||||
dynamic_cast<CastThunderClapAction*>(action) ||
|
||||
dynamic_cast<CastShockwaveAction*>(action) ||
|
||||
dynamic_cast<CastCleaveAction*>(action) ||
|
||||
dynamic_cast<CastGrowlAction*>(action) ||
|
||||
dynamic_cast<CastSwipeAction*>(action) ||
|
||||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
|
||||
dynamic_cast<CastAvengersShieldAction*>(action) ||
|
||||
dynamic_cast<CastConsecrationAction*>(action) ||
|
||||
dynamic_cast<CastDarkCommandAction*>(action) ||
|
||||
dynamic_cast<CastDeathAndDecayAction*>(action) ||
|
||||
dynamic_cast<CastPestilenceAction*>(action) ||
|
||||
dynamic_cast<CastBloodBoilAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressDisableAoeMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsDps(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
{
|
||||
if (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
||||
{
|
||||
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressControlMisdirectionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
{
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressWaitForDpsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||
if (!karathress)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<FathomLordKarathressMisdirectBossesToTanksAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
constexpr uint8 dpsWaitSeconds = 12;
|
||||
|
||||
auto it = karathressDpsWaitTimer.find(karathress->GetMap()->GetInstanceId());
|
||||
if (it == karathressDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(dynamic_cast<CastSpellAction*>(action) &&
|
||||
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "fathom-guard caribdis"))
|
||||
{
|
||||
if (dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
// Use Bloodlust/Heroism after the first Murloc spawn
|
||||
float MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "tidewalker lurker"))
|
||||
{
|
||||
if (dynamic_cast<CastHeroismAction*>(action) ||
|
||||
dynamic_cast<CastBloodlustAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float MorogrimTidewalkerDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "morogrim tidewalker"))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float MorogrimTidewalkerMaintainPhase2StackingMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
if (!tidewalker)
|
||||
return 1.0f;
|
||||
|
||||
if (tidewalker->GetHealthPct() < 25.0f)
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
// Wait until phase 3 to use Bloodlust/Heroism
|
||||
// Don't use other major cooldowns in Phase 1, either
|
||||
float LadyVashjDelayCooldownsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (bot->getClass() == CLASS_SHAMAN)
|
||||
{
|
||||
if (IsLadyVashjInPhase3(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (botAI->IsDps(bot) && IsLadyVashjInPhase1(botAI))
|
||||
{
|
||||
if (dynamic_cast<CastMetamorphosisAction*>(action) ||
|
||||
dynamic_cast<CastAdrenalineRushAction*>(action) ||
|
||||
dynamic_cast<CastBladeFlurryAction*>(action) ||
|
||||
dynamic_cast<CastIcyVeinsAction*>(action) ||
|
||||
dynamic_cast<CastColdSnapAction*>(action) ||
|
||||
dynamic_cast<CastArcanePowerAction*>(action) ||
|
||||
dynamic_cast<CastPresenceOfMindAction*>(action) ||
|
||||
dynamic_cast<CastCombustionAction*>(action) ||
|
||||
dynamic_cast<CastRapidFireAction*>(action) ||
|
||||
dynamic_cast<CastReadinessAction*>(action) ||
|
||||
dynamic_cast<CastAvengingWrathAction*>(action) ||
|
||||
dynamic_cast<CastElementalMasteryAction*>(action) ||
|
||||
dynamic_cast<CastFeralSpiritAction*>(action) ||
|
||||
dynamic_cast<CastFireElementalTotemAction*>(action) ||
|
||||
dynamic_cast<CastFireElementalTotemMeleeAction*>(action) ||
|
||||
dynamic_cast<CastForceOfNatureAction*>(action) ||
|
||||
dynamic_cast<CastArmyOfTheDeadAction*>(action) ||
|
||||
dynamic_cast<CastSummonGargoyleAction*>(action) ||
|
||||
dynamic_cast<CastBerserkingAction*>(action) ||
|
||||
dynamic_cast<CastBloodFuryAction*>(action) ||
|
||||
dynamic_cast<UseTrinketAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjMaintainPhase1RangedSpreadMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
IsLadyVashjInPhase1(botAI))
|
||||
{
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action) ||
|
||||
dynamic_cast<CastDisengageAction*>(action) ||
|
||||
dynamic_cast<CastBlinkBackAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjStaticChargeStayAwayFromGroupMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsMainTank(bot) || !bot->HasAura(SPELL_STATIC_CHARGE))
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||
dynamic_cast<ReachTargetAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Bots should not loot the core with normal looting logic
|
||||
float LadyVashjDoNotLootTheTaintedCoreMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
{
|
||||
if (dynamic_cast<LootAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float LadyVashjCorePassersPrioritizePositioningMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||
!IsLadyVashjInPhase2(botAI))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<WipeAction*>(action) ||
|
||||
dynamic_cast<DestroyItemAction*>(action) ||
|
||||
dynamic_cast<LadyVashjDestroyTaintedCoreAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return 1.0f;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||
|
||||
auto hasCore = [](Player* player)
|
||||
{
|
||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
};
|
||||
|
||||
if (hasCore(bot))
|
||||
{
|
||||
if (!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (bot == designatedLooter)
|
||||
{
|
||||
if (!hasCore(bot))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == firstCorePasser)
|
||||
{
|
||||
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||
hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == secondCorePasser)
|
||||
{
|
||||
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot == thirdCorePasser)
|
||||
{
|
||||
if (hasCore(fourthCorePasser))
|
||||
return 1.0f;
|
||||
}
|
||||
else if (bot != fourthCorePasser)
|
||||
return 1.0f;
|
||||
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||
(bot == firstCorePasser || bot == secondCorePasser))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (AnyRecentCoreInInventory(group, botAI))
|
||||
{
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<LadyVashjPassTheTaintedCoreAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// All of phases 2 and 3 require a custom movement and targeting system
|
||||
// So the standard target selection system must be disabled
|
||||
float LadyVashjDisableAutomaticTargetingAndMovementModifier::GetValue(Action *action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (IsLadyVashjInPhase2(botAI))
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action) ||
|
||||
dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (!botAI->IsHeal(bot) && dynamic_cast<CastHealingSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||
if (enchanted && bot->GetVictim() == enchanted)
|
||||
{
|
||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsLadyVashjInPhase3(botAI))
|
||||
{
|
||||
if (dynamic_cast<DpsAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
Unit* enchanted = AI_VALUE2(Unit*, "find target", "enchanted elemental");
|
||||
Unit* strider = AI_VALUE2(Unit*, "find target", "coilfang strider");
|
||||
Unit* elite = AI_VALUE2(Unit*, "find target", "coilfang elite");
|
||||
if (enchanted || strider || elite)
|
||||
{
|
||||
if (dynamic_cast<TankAssistAction*>(action) ||
|
||||
dynamic_cast<FollowAction*>(action) ||
|
||||
dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (enchanted && bot->GetVictim() == enchanted)
|
||||
{
|
||||
if (dynamic_cast<CastDebuffSpellOnAttackerAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
236
src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h
Normal file
236
src/Ai/Raid/SerpentshrineCavern/Multiplier/RaidSSCMultipliers.h
Normal file
@@ -0,0 +1,236 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDSSCMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
// Trash
|
||||
|
||||
class UnderbogColossusEscapeToxicPoolMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
UnderbogColossusEscapeToxicPoolMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "underbog colossus escape toxic pool") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
class HydrossTheUnstableDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable wait for dps") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class HydrossTheUnstableControlMisdirectionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableControlMisdirectionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hydross the unstable control misdirection") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
class TheLurkerBelowStayAwayFromSpoutMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowStayAwayFromSpoutMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below stay away from spout") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheLurkerBelowMaintainRangedSpreadMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowMaintainRangedSpreadMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below maintain ranged spread") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class TheLurkerBelowDisableTankAssistMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowDisableTankAssistMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "the lurker below disable tank assist") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
class LeotherasTheBlindAvoidWhirlwindMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindAvoidWhirlwindMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind avoid whirlwind") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind melee dps avoid chaos blast") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindFocusOnInnerDemonMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindFocusOnInnerDemonMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind focus on inner demon") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind wait for dps") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "leotheras the blind delay bloodlust and heroism") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
class FathomLordKarathressDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressDisableAoeMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressDisableAoeMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress disable aoe") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressControlMisdirectionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressControlMisdirectionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress control misdirection") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressWaitForDpsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressWaitForDpsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress wait for dps") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "fathom-lord karathress caribdis tank healer maintain position") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
class MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker delay bloodlust and heroism") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker disable tank actions") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerMaintainPhase2StackingMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerMaintainPhase2StackingMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "morogrim tidewalker maintain phase2 stacking") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
class LadyVashjDelayCooldownsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjDelayCooldownsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj delay cooldowns") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjMaintainPhase1RangedSpreadMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjMaintainPhase1RangedSpreadMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj maintain phase1 ranged spread") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjStaticChargeStayAwayFromGroupMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjStaticChargeStayAwayFromGroupMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj static charge stay away from group") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjDoNotLootTheTaintedCoreMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjDoNotLootTheTaintedCoreMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj do not loot the tainted core") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjCorePassersPrioritizePositioningMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjCorePassersPrioritizePositioningMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj core passers prioritize positioning") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class LadyVashjDisableAutomaticTargetingAndMovementModifier : public Multiplier
|
||||
{
|
||||
public:
|
||||
LadyVashjDisableAutomaticTargetingAndMovementModifier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "lady vashj disable automatic targeting and movement") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
337
src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h
Normal file
337
src/Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h
Normal file
@@ -0,0 +1,337 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDSSCACTIONCONTEXT_H
|
||||
|
||||
#include "RaidSSCActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidSSCActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidSSCActionContext()
|
||||
{
|
||||
// General
|
||||
creators["serpent shrine cavern erase timers and trackers"] =
|
||||
&RaidSSCActionContext::serpent_shrine_cavern_erase_timers_and_trackers;
|
||||
|
||||
// Trash
|
||||
creators["underbog colossus escape toxic pool"] =
|
||||
&RaidSSCActionContext::underbog_colossus_escape_toxic_pool;
|
||||
|
||||
creators["greyheart tidecaller mark water elemental totem"] =
|
||||
&RaidSSCActionContext::greyheart_tidecaller_mark_water_elemental_totem;
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
creators["hydross the unstable position frost tank"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_position_frost_tank;
|
||||
|
||||
creators["hydross the unstable position nature tank"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_position_nature_tank;
|
||||
|
||||
creators["hydross the unstable prioritize elemental adds"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_prioritize_elemental_adds;
|
||||
|
||||
creators["hydross the unstable frost phase spread out"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_frost_phase_spread_out;
|
||||
|
||||
creators["hydross the unstable misdirect boss to tank"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_misdirect_boss_to_tank;
|
||||
|
||||
creators["hydross the unstable stop dps upon phase change"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_stop_dps_upon_phase_change;
|
||||
|
||||
creators["hydross the unstable manage timers"] =
|
||||
&RaidSSCActionContext::hydross_the_unstable_manage_timers;
|
||||
|
||||
// The Lurker Below
|
||||
creators["the lurker below run around behind boss"] =
|
||||
&RaidSSCActionContext::the_lurker_below_run_around_behind_boss;
|
||||
|
||||
creators["the lurker below position main tank"] =
|
||||
&RaidSSCActionContext::the_lurker_below_position_main_tank;
|
||||
|
||||
creators["the lurker below spread ranged in arc"] =
|
||||
&RaidSSCActionContext::the_lurker_below_spread_ranged_in_arc;
|
||||
|
||||
creators["the lurker below tanks pick up adds"] =
|
||||
&RaidSSCActionContext::the_lurker_below_tanks_pick_up_adds;
|
||||
|
||||
creators["the lurker below manage spout timer"] =
|
||||
&RaidSSCActionContext::the_lurker_below_manage_spout_timer;
|
||||
|
||||
// Leotheras the Blind
|
||||
creators["leotheras the blind target spellbinders"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_target_spellbinders;
|
||||
|
||||
creators["leotheras the blind demon form tank attack boss"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_demon_form_tank_attack_boss;
|
||||
|
||||
creators["leotheras the blind melee tanks don't attack demon form"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_melee_tanks_dont_attack_demon_form;
|
||||
|
||||
creators["leotheras the blind position ranged"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_position_ranged;
|
||||
|
||||
creators["leotheras the blind run away from whirlwind"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_run_away_from_whirlwind;
|
||||
|
||||
creators["leotheras the blind melee dps run away from boss"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_melee_dps_run_away_from_boss;
|
||||
|
||||
creators["leotheras the blind destroy inner demon"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_destroy_inner_demon;
|
||||
|
||||
creators["leotheras the blind final phase assign dps priority"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_final_phase_assign_dps_priority;
|
||||
|
||||
creators["leotheras the blind misdirect boss to demon form tank"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_misdirect_boss_to_demon_form_tank;
|
||||
|
||||
creators["leotheras the blind manage dps wait timers"] =
|
||||
&RaidSSCActionContext::leotheras_the_blind_manage_dps_wait_timers;
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
creators["fathom-lord karathress main tank position boss"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_main_tank_position_boss;
|
||||
|
||||
creators["fathom-lord karathress first assist tank position caribdis"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_first_assist_tank_position_caribdis;
|
||||
|
||||
creators["fathom-lord karathress second assist tank position sharkkis"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_second_assist_tank_position_sharkkis;
|
||||
|
||||
creators["fathom-lord karathress third assist tank position tidalvess"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_third_assist_tank_position_tidalvess;
|
||||
|
||||
creators["fathom-lord karathress position caribdis tank healer"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_position_caribdis_tank_healer;
|
||||
|
||||
creators["fathom-lord karathress misdirect bosses to tanks"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_misdirect_bosses_to_tanks;
|
||||
|
||||
creators["fathom-lord karathress assign dps priority"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_assign_dps_priority;
|
||||
|
||||
creators["fathom-lord karathress manage dps timer"] =
|
||||
&RaidSSCActionContext::fathom_lord_karathress_manage_dps_timer;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
creators["morogrim tidewalker misdirect boss to main tank"] =
|
||||
&RaidSSCActionContext::morogrim_tidewalker_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["morogrim tidewalker move boss to tank position"] =
|
||||
&RaidSSCActionContext::morogrim_tidewalker_move_boss_to_tank_position;
|
||||
|
||||
creators["morogrim tidewalker phase 2 reposition ranged"] =
|
||||
&RaidSSCActionContext::morogrim_tidewalker_phase_2_reposition_ranged;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
creators["lady vashj main tank position boss"] =
|
||||
&RaidSSCActionContext::lady_vashj_main_tank_position_boss;
|
||||
|
||||
creators["lady vashj phase 1 spread ranged in arc"] =
|
||||
&RaidSSCActionContext::lady_vashj_phase_1_spread_ranged_in_arc;
|
||||
|
||||
creators["lady vashj set grounding totem in main tank group"] =
|
||||
&RaidSSCActionContext::lady_vashj_set_grounding_totem_in_main_tank_group;
|
||||
|
||||
creators["lady vashj static charge move away from group"] =
|
||||
&RaidSSCActionContext::lady_vashj_static_charge_move_away_from_group;
|
||||
|
||||
creators["lady vashj misdirect boss to main tank"] =
|
||||
&RaidSSCActionContext::lady_vashj_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["lady vashj assign phase 2 and phase 3 dps priority"] =
|
||||
&RaidSSCActionContext::lady_vashj_assign_phase_2_and_phase_3_dps_priority;
|
||||
|
||||
creators["lady vashj misdirect strider to first assist tank"] =
|
||||
&RaidSSCActionContext::lady_vashj_misdirect_strider_to_first_assist_tank;
|
||||
|
||||
creators["lady vashj tank attack and move away strider"] =
|
||||
&RaidSSCActionContext::lady_vashj_tank_attack_and_move_away_strider;
|
||||
|
||||
creators["lady vashj loot tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_loot_tainted_core;
|
||||
|
||||
creators["lady vashj teleport to tainted elemental"] =
|
||||
&RaidSSCActionContext::lady_vashj_teleport_to_tainted_elemental;
|
||||
|
||||
creators["lady vashj pass the tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_pass_the_tainted_core;
|
||||
|
||||
creators["lady vashj destroy tainted core"] =
|
||||
&RaidSSCActionContext::lady_vashj_destroy_tainted_core;
|
||||
|
||||
creators["lady vashj erase core passing trackers"] =
|
||||
&RaidSSCActionContext::lady_vashj_erase_core_passing_trackers;
|
||||
|
||||
creators["lady vashj avoid toxic spores"] =
|
||||
&RaidSSCActionContext::lady_vashj_avoid_toxic_spores;
|
||||
|
||||
creators["lady vashj use free action abilities"] =
|
||||
&RaidSSCActionContext::lady_vashj_use_free_action_abilities;
|
||||
}
|
||||
|
||||
private:
|
||||
// General
|
||||
static Action* serpent_shrine_cavern_erase_timers_and_trackers(
|
||||
PlayerbotAI* botAI) { return new SerpentShrineCavernEraseTimersAndTrackersAction(botAI); }
|
||||
|
||||
// Trash
|
||||
static Action* underbog_colossus_escape_toxic_pool(
|
||||
PlayerbotAI* botAI) { return new UnderbogColossusEscapeToxicPoolAction(botAI); }
|
||||
|
||||
static Action* greyheart_tidecaller_mark_water_elemental_totem(
|
||||
PlayerbotAI* botAI) { return new GreyheartTidecallerMarkWaterElementalTotemAction(botAI); }
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
static Action* hydross_the_unstable_position_frost_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstablePositionFrostTankAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_position_nature_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstablePositionNatureTankAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_prioritize_elemental_adds(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstablePrioritizeElementalAddsAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_frost_phase_spread_out(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableFrostPhaseSpreadOutAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_misdirect_boss_to_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableMisdirectBossToTankAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_stop_dps_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableStopDpsUponPhaseChangeAction(botAI); }
|
||||
|
||||
static Action* hydross_the_unstable_manage_timers(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableManageTimersAction(botAI); }
|
||||
|
||||
// The Lurker Below
|
||||
static Action* the_lurker_below_run_around_behind_boss(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowRunAroundBehindBossAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_position_main_tank(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowPositionMainTankAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_spread_ranged_in_arc(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowSpreadRangedInArcAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_tanks_pick_up_adds(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowTanksPickUpAddsAction(botAI); }
|
||||
|
||||
static Action* the_lurker_below_manage_spout_timer(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowManageSpoutTimerAction(botAI); }
|
||||
|
||||
// Leotheras the Blind
|
||||
static Action* leotheras_the_blind_target_spellbinders(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindTargetSpellbindersAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_demon_form_tank_attack_boss(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankAttackBossAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_melee_tanks_dont_attack_demon_form(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeTanksDontAttackDemonFormAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_position_ranged(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindPositionRangedAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_run_away_from_whirlwind(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindRunAwayFromWhirlwindAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_melee_dps_run_away_from_boss(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindMeleeDpsRunAwayFromBossAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_destroy_inner_demon(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindDestroyInnerDemonAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_misdirect_boss_to_demon_form_tank(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindMisdirectBossToDemonFormTankAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_final_phase_assign_dps_priority(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindFinalPhaseAssignDpsPriorityAction(botAI); }
|
||||
|
||||
static Action* leotheras_the_blind_manage_dps_wait_timers(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindManageDpsWaitTimersAction(botAI); }
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
static Action* fathom_lord_karathress_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_first_assist_tank_position_caribdis(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressFirstAssistTankPositionCaribdisAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_second_assist_tank_position_sharkkis(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressSecondAssistTankPositionSharkkisAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_third_assist_tank_position_tidalvess(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressThirdAssistTankPositionTidalvessAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_position_caribdis_tank_healer(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressPositionCaribdisTankHealerAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_misdirect_bosses_to_tanks(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressMisdirectBossesToTanksAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_assign_dps_priority(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressAssignDpsPriorityAction(botAI); }
|
||||
|
||||
static Action* fathom_lord_karathress_manage_dps_timer(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressManageDpsTimerAction(botAI); }
|
||||
|
||||
// Morogrim Tidewalker
|
||||
static Action* morogrim_tidewalker_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* morogrim_tidewalker_move_boss_to_tank_position(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerMoveBossToTankPositionAction(botAI); }
|
||||
|
||||
static Action* morogrim_tidewalker_phase_2_reposition_ranged(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerPhase2RepositionRangedAction(botAI); }
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
static Action* lady_vashj_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new LadyVashjMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_phase_1_spread_ranged_in_arc(
|
||||
PlayerbotAI* botAI) { return new LadyVashjPhase1SpreadRangedInArcAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_set_grounding_totem_in_main_tank_group(
|
||||
PlayerbotAI* botAI) { return new LadyVashjSetGroundingTotemInMainTankGroupAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_static_charge_move_away_from_group(
|
||||
PlayerbotAI* botAI) { return new LadyVashjStaticChargeMoveAwayFromGroupAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new LadyVashjMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_assign_phase_2_and_phase_3_dps_priority(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAssignPhase2AndPhase3DpsPriorityAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_misdirect_strider_to_first_assist_tank(
|
||||
PlayerbotAI* botAI) { return new LadyVashjMisdirectStriderToFirstAssistTankAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_tank_attack_and_move_away_strider(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTankAttackAndMoveAwayStriderAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_teleport_to_tainted_elemental(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTeleportToTaintedElementalAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_loot_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjLootTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_pass_the_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjPassTheTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_destroy_tainted_core(
|
||||
PlayerbotAI* botAI) { return new LadyVashjDestroyTaintedCoreAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_erase_core_passing_trackers(
|
||||
PlayerbotAI* botAI) { return new LadyVashjEraseCorePassingTrackersAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_avoid_toxic_spores(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAvoidToxicSporesAction(botAI); }
|
||||
|
||||
static Action* lady_vashj_use_free_action_abilities(
|
||||
PlayerbotAI* botAI) { return new LadyVashjUseFreeActionAbilitiesAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
325
src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h
Normal file
325
src/Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h
Normal file
@@ -0,0 +1,325 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidSSCTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
|
||||
class RaidSSCTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidSSCTriggerContext()
|
||||
{
|
||||
// General
|
||||
creators["serpent shrine cavern bot is not in combat"] =
|
||||
&RaidSSCTriggerContext::serpent_shrine_cavern_bot_is_not_in_combat;
|
||||
|
||||
// Trash
|
||||
creators["underbog colossus spawned toxic pool after death"] =
|
||||
&RaidSSCTriggerContext::underbog_colossus_spawned_toxic_pool_after_death;
|
||||
|
||||
creators["greyheart tidecaller water elemental totem spawned"] =
|
||||
&RaidSSCTriggerContext::greyheart_tidecaller_water_elemental_totem_spawned;
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
creators["hydross the unstable bot is frost tank"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_bot_is_frost_tank;
|
||||
|
||||
creators["hydross the unstable bot is nature tank"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_bot_is_nature_tank;
|
||||
|
||||
creators["hydross the unstable elementals spawned"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_elementals_spawned;
|
||||
|
||||
creators["hydross the unstable danger from water tombs"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_danger_from_water_tombs;
|
||||
|
||||
creators["hydross the unstable tank needs aggro upon phase change"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_tank_needs_aggro_upon_phase_change;
|
||||
|
||||
creators["hydross the unstable aggro resets upon phase change"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_aggro_resets_upon_phase_change;
|
||||
|
||||
creators["hydross the unstable need to manage timers"] =
|
||||
&RaidSSCTriggerContext::hydross_the_unstable_need_to_manage_timers;
|
||||
|
||||
// The Lurker Below
|
||||
creators["the lurker below spout is active"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_spout_is_active;
|
||||
|
||||
creators["the lurker below boss is active for main tank"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_boss_is_active_for_main_tank;
|
||||
|
||||
creators["the lurker below boss casts geyser"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_boss_casts_geyser;
|
||||
|
||||
creators["the lurker below boss is submerged"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_boss_is_submerged;
|
||||
|
||||
creators["the lurker below need to prepare timer for spout"] =
|
||||
&RaidSSCTriggerContext::the_lurker_below_need_to_prepare_timer_for_spout;
|
||||
|
||||
// Leotheras the Blind
|
||||
creators["leotheras the blind boss is inactive"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_is_inactive;
|
||||
|
||||
creators["leotheras the blind boss transformed into demon form"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_transformed_into_demon_form;
|
||||
|
||||
creators["leotheras the blind only warlock should tank demon form"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_only_warlock_should_tank_demon_form;
|
||||
|
||||
creators["leotheras the blind boss engaged by ranged"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_engaged_by_ranged;
|
||||
|
||||
creators["leotheras the blind boss channeling whirlwind"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_channeling_whirlwind;
|
||||
|
||||
creators["leotheras the blind bot has too many chaos blast stacks"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_bot_has_too_many_chaos_blast_stacks;
|
||||
|
||||
creators["leotheras the blind inner demon has awakened"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_inner_demon_has_awakened;
|
||||
|
||||
creators["leotheras the blind entered final phase"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_entered_final_phase;
|
||||
|
||||
creators["leotheras the blind demon form tank needs aggro"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_demon_form_tank_needs_aggro;
|
||||
|
||||
creators["leotheras the blind boss wipes aggro upon phase change"] =
|
||||
&RaidSSCTriggerContext::leotheras_the_blind_boss_wipes_aggro_upon_phase_change;
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
creators["fathom-lord karathress boss engaged by main tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_boss_engaged_by_main_tank;
|
||||
|
||||
creators["fathom-lord karathress caribdis engaged by first assist tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_caribdis_engaged_by_first_assist_tank;
|
||||
|
||||
creators["fathom-lord karathress sharkkis engaged by second assist tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank;
|
||||
|
||||
creators["fathom-lord karathress tidalvess engaged by third assist tank"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank;
|
||||
|
||||
creators["fathom-lord karathress caribdis tank needs dedicated healer"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_caribdis_tank_needs_dedicated_healer;
|
||||
|
||||
creators["fathom-lord karathress pulling bosses"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_pulling_bosses;
|
||||
|
||||
creators["fathom-lord karathress determining kill order"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_determining_kill_order;
|
||||
|
||||
creators["fathom-lord karathress tanks need to establish aggro"] =
|
||||
&RaidSSCTriggerContext::fathom_lord_karathress_tanks_need_to_establish_aggro;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
creators["morogrim tidewalker boss engaged by main tank"] =
|
||||
&RaidSSCTriggerContext::morogrim_tidewalker_boss_engaged_by_main_tank;
|
||||
|
||||
creators["morogrim tidewalker pulling boss"] =
|
||||
&RaidSSCTriggerContext::morogrim_tidewalker_pulling_boss;
|
||||
|
||||
creators["morogrim tidewalker water globules are incoming"] =
|
||||
&RaidSSCTriggerContext::morogrim_tidewalker_water_globules_are_incoming;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
creators["lady vashj boss engaged by main tank"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_boss_engaged_by_main_tank;
|
||||
|
||||
creators["lady vashj boss engaged by ranged in phase 1"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_boss_engaged_by_ranged_in_phase_1;
|
||||
|
||||
creators["lady vashj casts shock blast on highest aggro"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_casts_shock_blast_on_highest_aggro;
|
||||
|
||||
creators["lady vashj bot has static charge"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_bot_has_static_charge;
|
||||
|
||||
creators["lady vashj pulling boss in phase 1 and phase 3"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_pulling_boss_in_phase_1_and_phase_3;
|
||||
|
||||
creators["lady vashj adds spawn in phase 2 and phase 3"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_adds_spawn_in_phase_2_and_phase_3;
|
||||
|
||||
creators["lady vashj coilfang strider is approaching"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_coilfang_strider_is_approaching;
|
||||
|
||||
creators["lady vashj tainted elemental cheat"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_elemental_cheat;
|
||||
|
||||
creators["lady vashj tainted core was looted"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_core_was_looted;
|
||||
|
||||
creators["lady vashj tainted core is unusable"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_tainted_core_is_unusable;
|
||||
|
||||
creators["lady vashj need to reset core passing trackers"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_need_to_reset_core_passing_trackers;
|
||||
|
||||
creators["lady vashj toxic sporebats are spewing poison clouds"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_toxic_sporebats_are_spewing_poison_clouds;
|
||||
|
||||
creators["lady vashj bot is entangled in toxic spores or static charge"] =
|
||||
&RaidSSCTriggerContext::lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge;
|
||||
}
|
||||
|
||||
private:
|
||||
// General
|
||||
static Trigger* serpent_shrine_cavern_bot_is_not_in_combat(
|
||||
PlayerbotAI* botAI) { return new SerpentShrineCavernBotIsNotInCombatTrigger(botAI); }
|
||||
|
||||
// Trash
|
||||
static Trigger* underbog_colossus_spawned_toxic_pool_after_death(
|
||||
PlayerbotAI* botAI) { return new UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(botAI); }
|
||||
|
||||
static Trigger* greyheart_tidecaller_water_elemental_totem_spawned(
|
||||
PlayerbotAI* botAI) { return new GreyheartTidecallerWaterElementalTotemSpawnedTrigger(botAI); }
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
static Trigger* hydross_the_unstable_bot_is_frost_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsFrostTankTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_bot_is_nature_tank(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableBotIsNatureTankTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_elementals_spawned(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableElementalsSpawnedTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_danger_from_water_tombs(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableDangerFromWaterTombsTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_tank_needs_aggro_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_aggro_resets_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(botAI); }
|
||||
|
||||
static Trigger* hydross_the_unstable_need_to_manage_timers(
|
||||
PlayerbotAI* botAI) { return new HydrossTheUnstableNeedToManageTimersTrigger(botAI); }
|
||||
|
||||
// The Lurker Below
|
||||
static Trigger* the_lurker_below_spout_is_active(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowSpoutIsActiveTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_boss_is_active_for_main_tank(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowBossIsActiveForMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_boss_casts_geyser(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowBossCastsGeyserTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_boss_is_submerged(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowBossIsSubmergedTrigger(botAI); }
|
||||
|
||||
static Trigger* the_lurker_below_need_to_prepare_timer_for_spout(
|
||||
PlayerbotAI* botAI) { return new TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(botAI); }
|
||||
|
||||
// Leotheras the Blind
|
||||
static Trigger* leotheras_the_blind_boss_is_inactive(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossIsInactiveTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_transformed_into_demon_form(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossTransformedIntoDemonFormTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_only_warlock_should_tank_demon_form(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_engaged_by_ranged(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossEngagedByRangedTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_channeling_whirlwind(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossChannelingWhirlwindTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_bot_has_too_many_chaos_blast_stacks(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_inner_demon_has_awakened(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindInnerDemonHasAwakenedTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_entered_final_phase(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindEnteredFinalPhaseTrigger(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_demon_form_tank_needs_aggro(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindDemonFormTankNeedsAggro(botAI); }
|
||||
|
||||
static Trigger* leotheras_the_blind_boss_wipes_aggro_upon_phase_change(
|
||||
PlayerbotAI* botAI) { return new LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(botAI); }
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
static Trigger* fathom_lord_karathress_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_caribdis_engaged_by_first_assist_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_sharkkis_engaged_by_second_assist_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_tidalvess_engaged_by_third_assist_tank(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_caribdis_tank_needs_dedicated_healer(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_pulling_bosses(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressPullingBossesTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_determining_kill_order(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressDeterminingKillOrderTrigger(botAI); }
|
||||
|
||||
static Trigger* fathom_lord_karathress_tanks_need_to_establish_aggro(
|
||||
PlayerbotAI* botAI) { return new FathomLordKarathressTanksNeedToEstablishAggroTrigger(botAI); }
|
||||
|
||||
// Morogrim Tidewalker
|
||||
static Trigger* morogrim_tidewalker_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* morogrim_tidewalker_pulling_boss(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerPullingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* morogrim_tidewalker_water_globules_are_incoming(
|
||||
PlayerbotAI* botAI) { return new MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(botAI); }
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
static Trigger* lady_vashj_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_boss_engaged_by_ranged_in_phase_1(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBossEngagedByRangedInPhase1Trigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_casts_shock_blast_on_highest_aggro(
|
||||
PlayerbotAI* botAI) { return new LadyVashjCastsShockBlastOnHighestAggroTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_bot_has_static_charge(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBotHasStaticChargeTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_pulling_boss_in_phase_1_and_phase_3(
|
||||
PlayerbotAI* botAI) { return new LadyVashjPullingBossInPhase1AndPhase3Trigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_adds_spawn_in_phase_2_and_phase_3(
|
||||
PlayerbotAI* botAI) { return new LadyVashjAddsSpawnInPhase2AndPhase3Trigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_coilfang_strider_is_approaching(
|
||||
PlayerbotAI* botAI) { return new LadyVashjCoilfangStriderIsApproachingTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_tainted_elemental_cheat(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedElementalCheatTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_tainted_core_was_looted(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreWasLootedTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_tainted_core_is_unusable(
|
||||
PlayerbotAI* botAI) { return new LadyVashjTaintedCoreIsUnusableTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_need_to_reset_core_passing_trackers(
|
||||
PlayerbotAI* botAI) { return new LadyVashjNeedToResetCorePassingTrackersTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_toxic_sporebats_are_spewing_poison_clouds(
|
||||
PlayerbotAI* botAI) { return new LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(botAI); }
|
||||
|
||||
static Trigger* lady_vashj_bot_is_entangled_in_toxic_spores_or_static_charge(
|
||||
PlayerbotAI* botAI) { return new LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
206
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp
Normal file
206
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidSSCMultipliers.h"
|
||||
|
||||
void RaidSSCStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// General
|
||||
triggers.push_back(new TriggerNode("serpent shrine cavern bot is not in combat", {
|
||||
NextAction("serpent shrine cavern erase timers and trackers", ACTION_EMERGENCY + 11) }));
|
||||
|
||||
// Trash Mobs
|
||||
triggers.push_back(new TriggerNode("underbog colossus spawned toxic pool after death", {
|
||||
NextAction("underbog colossus escape toxic pool", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("greyheart tidecaller water elemental totem spawned", {
|
||||
NextAction("greyheart tidecaller mark water elemental totem", ACTION_RAID + 1) }));
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
triggers.push_back(new TriggerNode("hydross the unstable bot is frost tank", {
|
||||
NextAction("hydross the unstable position frost tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable bot is nature tank", {
|
||||
NextAction("hydross the unstable position nature tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable elementals spawned", {
|
||||
NextAction("hydross the unstable prioritize elemental adds", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable danger from water tombs", {
|
||||
NextAction("hydross the unstable frost phase spread out", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable tank needs aggro upon phase change", {
|
||||
NextAction("hydross the unstable misdirect boss to tank", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable aggro resets upon phase change", {
|
||||
NextAction("hydross the unstable stop dps upon phase change", ACTION_EMERGENCY + 9) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("hydross the unstable need to manage timers", {
|
||||
NextAction("hydross the unstable manage timers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// The Lurker Below
|
||||
triggers.push_back(new TriggerNode("the lurker below spout is active", {
|
||||
NextAction("the lurker below run around behind boss", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below boss is active for main tank", {
|
||||
NextAction("the lurker below position main tank", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below boss casts geyser", {
|
||||
NextAction("the lurker below spread ranged in arc", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below boss is submerged", {
|
||||
NextAction("the lurker below tanks pick up adds", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("the lurker below need to prepare timer for spout", {
|
||||
NextAction("the lurker below manage spout timer", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// Leotheras the Blind
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss is inactive", {
|
||||
NextAction("leotheras the blind target spellbinders", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss transformed into demon form", {
|
||||
NextAction("leotheras the blind demon form tank attack boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind only warlock should tank demon form", {
|
||||
NextAction("leotheras the blind melee tanks don't attack demon form", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss engaged by ranged", {
|
||||
NextAction("leotheras the blind position ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss channeling whirlwind", {
|
||||
NextAction("leotheras the blind run away from whirlwind", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind bot has too many chaos blast stacks", {
|
||||
NextAction("leotheras the blind melee dps run away from boss", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind inner demon has awakened", {
|
||||
NextAction("leotheras the blind destroy inner demon", ACTION_EMERGENCY + 7) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind entered final phase", {
|
||||
NextAction("leotheras the blind final phase assign dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind demon form tank needs aggro", {
|
||||
NextAction("leotheras the blind misdirect boss to demon form tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("leotheras the blind boss wipes aggro upon phase change", {
|
||||
NextAction("leotheras the blind manage dps wait timers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress boss engaged by main tank", {
|
||||
NextAction("fathom-lord karathress main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress caribdis engaged by first assist tank", {
|
||||
NextAction("fathom-lord karathress first assist tank position caribdis", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress sharkkis engaged by second assist tank", {
|
||||
NextAction("fathom-lord karathress second assist tank position sharkkis", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress tidalvess engaged by third assist tank", {
|
||||
NextAction("fathom-lord karathress third assist tank position tidalvess", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress caribdis tank needs dedicated healer", {
|
||||
NextAction("fathom-lord karathress position caribdis tank healer", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress pulling bosses", {
|
||||
NextAction("fathom-lord karathress misdirect bosses to tanks", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress determining kill order", {
|
||||
NextAction("fathom-lord karathress assign dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("fathom-lord karathress tanks need to establish aggro", {
|
||||
NextAction("fathom-lord karathress manage dps timer", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
// Morogrim Tidewalker
|
||||
triggers.push_back(new TriggerNode("morogrim tidewalker boss engaged by main tank", {
|
||||
NextAction("morogrim tidewalker move boss to tank position", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("morogrim tidewalker water globules are incoming", {
|
||||
NextAction("morogrim tidewalker phase 2 reposition ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("morogrim tidewalker pulling boss", {
|
||||
NextAction("morogrim tidewalker misdirect boss to main tank", ACTION_RAID + 1) }));
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
triggers.push_back(new TriggerNode("lady vashj boss engaged by main tank", {
|
||||
NextAction("lady vashj main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj boss engaged by ranged in phase 1", {
|
||||
NextAction("lady vashj phase 1 spread ranged in arc", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj casts shock blast on highest aggro", {
|
||||
NextAction("lady vashj set grounding totem in main tank group", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj bot has static charge", {
|
||||
NextAction("lady vashj static charge move away from group", ACTION_EMERGENCY + 7) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj pulling boss in phase 1 and phase 3", {
|
||||
NextAction("lady vashj misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted elemental cheat", {
|
||||
NextAction("lady vashj teleport to tainted elemental", ACTION_EMERGENCY + 10),
|
||||
NextAction("lady vashj loot tainted core", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted core was looted", {
|
||||
NextAction("lady vashj pass the tainted core", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj tainted core is unusable", {
|
||||
NextAction("lady vashj destroy tainted core", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj need to reset core passing trackers", {
|
||||
NextAction("lady vashj erase core passing trackers", ACTION_EMERGENCY + 10) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj adds spawn in phase 2 and phase 3", {
|
||||
NextAction("lady vashj assign phase 2 and phase 3 dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj coilfang strider is approaching", {
|
||||
NextAction("lady vashj misdirect strider to first assist tank", ACTION_EMERGENCY + 2),
|
||||
NextAction("lady vashj tank attack and move away strider", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj toxic sporebats are spewing poison clouds", {
|
||||
NextAction("lady vashj avoid toxic spores", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("lady vashj bot is entangled in toxic spores or static charge", {
|
||||
NextAction("lady vashj use free action abilities", ACTION_EMERGENCY + 7) }));
|
||||
}
|
||||
|
||||
void RaidSSCStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
// Trash Mobs
|
||||
multipliers.push_back(new UnderbogColossusEscapeToxicPoolMultiplier(botAI));
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
multipliers.push_back(new HydrossTheUnstableDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new HydrossTheUnstableWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new HydrossTheUnstableControlMisdirectionMultiplier(botAI));
|
||||
|
||||
// The Lurker Below
|
||||
multipliers.push_back(new TheLurkerBelowStayAwayFromSpoutMultiplier(botAI));
|
||||
multipliers.push_back(new TheLurkerBelowMaintainRangedSpreadMultiplier(botAI));
|
||||
multipliers.push_back(new TheLurkerBelowDisableTankAssistMultiplier(botAI));
|
||||
|
||||
// Leotheras the Blind
|
||||
multipliers.push_back(new LeotherasTheBlindAvoidWhirlwindMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindMeleeDpsAvoidChaosBlastMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindFocusOnInnerDemonMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new LeotherasTheBlindDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
multipliers.push_back(new FathomLordKarathressDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressDisableAoeMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressControlMisdirectionMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressWaitForDpsMultiplier(botAI));
|
||||
multipliers.push_back(new FathomLordKarathressCaribdisTankHealerMaintainPositionMultiplier(botAI));
|
||||
|
||||
// Morogrim Tidewalker
|
||||
multipliers.push_back(new MorogrimTidewalkerDelayBloodlustAndHeroismMultiplier(botAI));
|
||||
multipliers.push_back(new MorogrimTidewalkerDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new MorogrimTidewalkerMaintainPhase2StackingMultiplier(botAI));
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
multipliers.push_back(new LadyVashjDelayCooldownsMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjMaintainPhase1RangedSpreadMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjStaticChargeStayAwayFromGroupMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjDoNotLootTheTaintedCoreMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjCorePassersPrioritizePositioningMultiplier(botAI));
|
||||
multipliers.push_back(new LadyVashjDisableAutomaticTargetingAndMovementModifier(botAI));
|
||||
}
|
||||
18
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h
Normal file
18
src/Ai/Raid/SerpentshrineCavern/Strategy/RaidSSCStrategy.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
#define _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidSSCStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidSSCStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "ssc"; }
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
670
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp
Normal file
670
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.cpp
Normal file
@@ -0,0 +1,670 @@
|
||||
#include "RaidSSCTriggers.h"
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "RaidSSCActions.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Corpse.h"
|
||||
#include "LootObjectStack.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace SerpentShrineCavernHelpers;
|
||||
|
||||
// General
|
||||
bool SerpentShrineCavernBotIsNotInCombatTrigger::IsActive()
|
||||
{
|
||||
return !bot->IsInCombat();
|
||||
}
|
||||
|
||||
// Trash Mobs
|
||||
|
||||
bool UnderbogColossusSpawnedToxicPoolAfterDeathTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_TOXIC_POOL);
|
||||
}
|
||||
|
||||
bool GreyheartTidecallerWaterElementalTotemSpawnedTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsDps(bot) &&
|
||||
GetFirstAliveUnitByEntry(botAI, NPC_WATER_ELEMENTAL_TOTEM);
|
||||
}
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
bool HydrossTheUnstableBotIsFrostTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableBotIsNatureTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableElementalsSpawnedTrigger::IsActive()
|
||||
{
|
||||
Unit* hydross = AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
if (hydross && hydross->GetHealthPct() < 10.0f)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "pure spawn of hydross") &&
|
||||
!AI_VALUE2(Unit*, "find target", "tainted spawn of hydross"))
|
||||
return false;
|
||||
|
||||
return !botAI->IsHeal(bot) && !botAI->IsMainTank(bot) &&
|
||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableDangerFromWaterTombsTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsRanged(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
return bot->getClass() == CLASS_HUNTER &&
|
||||
AI_VALUE2(Unit*, "find target", "hydross the unstable");
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableAggroResetsUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "hydross the unstable"))
|
||||
return false;
|
||||
|
||||
return bot->getClass() != CLASS_HUNTER &&
|
||||
!botAI->IsHeal(bot) &&
|
||||
!botAI->IsMainTank(bot) &&
|
||||
!botAI->IsAssistTankOfIndex(bot, 0, true);
|
||||
}
|
||||
|
||||
bool HydrossTheUnstableNeedToManageTimersTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "hydross the unstable") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
bool TheLurkerBelowSpoutIsActiveTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
return it != lurkerSpoutTimer.end() && it->second > now;
|
||||
}
|
||||
|
||||
bool TheLurkerBelowBossIsActiveForMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED &&
|
||||
(it == lurkerSpoutTimer.end() || it->second <= now);
|
||||
}
|
||||
|
||||
bool TheLurkerBelowBossCastsGeyserTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker)
|
||||
return false;
|
||||
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lurkerSpoutTimer.find(lurker->GetMap()->GetInstanceId());
|
||||
return lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED &&
|
||||
(it == lurkerSpoutTimer.end() || it->second <= now);
|
||||
}
|
||||
|
||||
// Trigger will be active only if there are at least 3 tanks in the raid
|
||||
bool TheLurkerBelowBossIsSubmergedTrigger::IsActive()
|
||||
{
|
||||
Unit* lurker = AI_VALUE2(Unit*, "find target", "the lurker below");
|
||||
if (!lurker || lurker->getStandState() != UNIT_STAND_STATE_SUBMERGED)
|
||||
return false;
|
||||
|
||||
Player* mainTank = nullptr;
|
||||
Player* firstAssistTank = nullptr;
|
||||
Player* secondAssistTank = nullptr;
|
||||
|
||||
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())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (!mainTank && memberAI->IsMainTank(member))
|
||||
mainTank = member;
|
||||
else if (!firstAssistTank && memberAI->IsAssistTankOfIndex(member, 0, true))
|
||||
firstAssistTank = member;
|
||||
else if (!secondAssistTank && memberAI->IsAssistTankOfIndex(member, 1, true))
|
||||
secondAssistTank = member;
|
||||
}
|
||||
|
||||
if (!mainTank || !firstAssistTank || !secondAssistTank)
|
||||
return false;
|
||||
|
||||
return bot == mainTank || bot == firstAssistTank || bot == secondAssistTank;
|
||||
}
|
||||
|
||||
bool TheLurkerBelowNeedToPrepareTimerForSpoutTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "the lurker below") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
bool LeotherasTheBlindBossIsInactiveTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "greyheart spellbinder");
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossTransformedIntoDemonFormTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (GetLeotherasDemonFormTank(bot) != bot)
|
||||
return false;
|
||||
|
||||
return GetActiveLeotherasDemon(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsRanged(bot) || !botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "leotheras the blind"))
|
||||
return false;
|
||||
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (!GetLeotherasDemonFormTank(bot))
|
||||
return false;
|
||||
|
||||
return GetPhase2LeotherasDemon(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras)
|
||||
return false;
|
||||
|
||||
return !leotheras->HasAura(SPELL_LEOTHERAS_BANISHED) &&
|
||||
!leotheras->HasAura(SPELL_WHIRLWIND) &&
|
||||
!leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossChannelingWhirlwindTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsTank(bot) && botAI->IsMelee(bot))
|
||||
return false;
|
||||
|
||||
Unit* leotheras = AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
if (!leotheras || leotheras->HasAura(SPELL_LEOTHERAS_BANISHED))
|
||||
return false;
|
||||
|
||||
return leotheras->HasAura(SPELL_WHIRLWIND) ||
|
||||
leotheras->HasAura(SPELL_WHIRLWIND_CHANNEL);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Aura* chaosBlast = bot->GetAura(SPELL_CHAOS_BLAST);
|
||||
if (!chaosBlast || chaosBlast->GetStackAmount() < 5)
|
||||
return false;
|
||||
|
||||
if (!GetLeotherasDemonFormTank(bot) && botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
return GetPhase2LeotherasDemon(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindInnerDemonHasAwakenedTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(SPELL_INSIDIOUS_WHISPER) &&
|
||||
GetLeotherasDemonFormTank(bot) != bot;
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindEnteredFinalPhaseTrigger::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
if (GetLeotherasDemonFormTank(bot) == bot)
|
||||
return false;
|
||||
|
||||
return GetPhase3LeotherasDemon(botAI) &&
|
||||
GetLeotherasHuman(botAI);
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindDemonFormTankNeedsAggro::IsActive()
|
||||
{
|
||||
if (bot->HasAura(SPELL_INSIDIOUS_WHISPER))
|
||||
return false;
|
||||
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind");
|
||||
}
|
||||
|
||||
bool LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "leotheras the blind") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
bool FathomLordKarathressBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||
botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard caribdis") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 0, false);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 1, false);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess") &&
|
||||
botAI->IsAssistTankOfIndex(bot, 2, false);
|
||||
}
|
||||
|
||||
bool FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger::IsActive()
|
||||
{
|
||||
Unit* caribdis = AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
if (!caribdis)
|
||||
return false;
|
||||
|
||||
if (!botAI->IsAssistHealOfIndex(bot, 0, true))
|
||||
return false;
|
||||
|
||||
Player* firstAssistTank = nullptr;
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (botAI->IsAssistTankOfIndex(member, 0, false))
|
||||
{
|
||||
firstAssistTank = member;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstAssistTank;
|
||||
}
|
||||
|
||||
bool FathomLordKarathressPullingBossesTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* karathress = AI_VALUE2(Unit*, "find target", "fathom-lord karathress");
|
||||
return karathress && karathress->GetHealthPct() > 98.0f;
|
||||
}
|
||||
|
||||
bool FathomLordKarathressDeterminingKillOrderTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "fathom-lord karathress"))
|
||||
return false;
|
||||
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
if (botAI->IsDps(bot))
|
||||
return true;
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 0, false))
|
||||
return !AI_VALUE2(Unit*, "find target", "fathom-guard caribdis");
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 1, false))
|
||||
return !AI_VALUE2(Unit*, "find target", "fathom-guard sharkkis");
|
||||
else if (botAI->IsAssistTankOfIndex(bot, 2, false))
|
||||
return !AI_VALUE2(Unit*, "find target", "fathom-guard tidalvess");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FathomLordKarathressTanksNeedToEstablishAggroTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "fathom-lord karathress") &&
|
||||
IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr);
|
||||
}
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
bool MorogrimTidewalkerPullingBossTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
return tidewalker && tidewalker->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool MorogrimTidewalkerBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "morogrim tidewalker") &&
|
||||
botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool MorogrimTidewalkerWaterGlobulesAreIncomingTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* tidewalker = AI_VALUE2(Unit*, "find target", "morogrim tidewalker");
|
||||
return tidewalker && tidewalker->GetHealthPct() < 25.0f;
|
||||
}
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
bool LadyVashjBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
!IsLadyVashjInPhase2(botAI) && botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool LadyVashjBossEngagedByRangedInPhase1Trigger::IsActive()
|
||||
{
|
||||
return botAI->IsRanged(bot) && IsLadyVashjInPhase1(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjCastsShockBlastOnHighestAggroTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") ||
|
||||
IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
if (!IsMainTankInSameSubgroup(bot))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LadyVashjBotHasStaticChargeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->HasAura(SPELL_STATIC_CHARGE))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjPullingBossInPhase1AndPhase3Trigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
return (vashj->GetHealthPct() <= 100.0f && vashj->GetHealthPct() > 90.0f) ||
|
||||
(!vashj->HasUnitState(UNIT_STATE_ROOT) && vashj->GetHealthPct() <= 50.0f &&
|
||||
vashj->GetHealthPct() > 40.0f);
|
||||
}
|
||||
|
||||
bool LadyVashjAddsSpawnInPhase2AndPhase3Trigger::IsActive()
|
||||
{
|
||||
if (botAI->IsHeal(bot))
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "lady vashj") &&
|
||||
!IsLadyVashjInPhase1(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjCoilfangStriderIsApproachingTrigger::IsActive()
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "coilfang strider");
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedElementalCheatTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
bool taintedPresent = false;
|
||||
Unit* taintedUnit = AI_VALUE2(Unit*, "find target", "tainted elemental");
|
||||
if (taintedUnit)
|
||||
taintedPresent = true;
|
||||
else
|
||||
{
|
||||
GuidVector corpses = AI_VALUE(GuidVector, "nearest corpses");
|
||||
for (auto const& guid : corpses)
|
||||
{
|
||||
LootObject loot(bot, guid);
|
||||
WorldObject* object = loot.GetWorldObject(bot);
|
||||
if (!object)
|
||||
continue;
|
||||
|
||||
if (Creature* creature = object->ToCreature())
|
||||
{
|
||||
if (creature->GetEntry() == NPC_TAINTED_ELEMENTAL && !creature->IsAlive())
|
||||
{
|
||||
taintedPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!taintedPresent)
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
return (GetDesignatedCoreLooter(group, botAI) == bot &&
|
||||
!bot->HasItemCount(ITEM_TAINTED_CORE, 1, false));
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedCoreWasLootedTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj") || !IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
Player* fourthCorePasser = GetFourthTaintedCorePasser(group, botAI);
|
||||
|
||||
auto hasCore = [](Player* player) -> bool
|
||||
{
|
||||
return player && player->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
};
|
||||
|
||||
if (bot == designatedLooter)
|
||||
{
|
||||
if (!hasCore(bot))
|
||||
return false;
|
||||
}
|
||||
else if (bot == firstCorePasser)
|
||||
{
|
||||
if (hasCore(secondCorePasser) || hasCore(thirdCorePasser) ||
|
||||
hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot == secondCorePasser)
|
||||
{
|
||||
if (hasCore(thirdCorePasser) || hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot == thirdCorePasser)
|
||||
{
|
||||
if (hasCore(fourthCorePasser))
|
||||
return false;
|
||||
}
|
||||
else if (bot != fourthCorePasser)
|
||||
return false;
|
||||
|
||||
if (AnyRecentCoreInInventory(group, botAI))
|
||||
return true;
|
||||
|
||||
// First and second passers move to positions as soon as the elemental appears
|
||||
if (AI_VALUE2(Unit*, "find target", "tainted elemental") &&
|
||||
(bot == firstCorePasser || bot == secondCorePasser))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjTaintedCoreIsUnusableTrigger::IsActive()
|
||||
{
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
if (!IsLadyVashjInPhase2(botAI))
|
||||
return bot->HasItemCount(ITEM_TAINTED_CORE, 1, false);
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* coreHandlers[] =
|
||||
{
|
||||
GetDesignatedCoreLooter(group, botAI),
|
||||
GetFirstTaintedCorePasser(group, botAI),
|
||||
GetSecondTaintedCorePasser(group, botAI),
|
||||
GetThirdTaintedCorePasser(group, botAI),
|
||||
GetFourthTaintedCorePasser(group, botAI)
|
||||
};
|
||||
|
||||
if (bot->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
{
|
||||
for (Player* coreHandler : coreHandlers)
|
||||
{
|
||||
if (coreHandler && bot == coreHandler)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LadyVashjNeedToResetCorePassingTrackersTrigger::IsActive()
|
||||
{
|
||||
Unit* vashj = AI_VALUE2(Unit*, "find target", "lady vashj");
|
||||
if (!vashj || IsLadyVashjInPhase2(botAI))
|
||||
return false;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
return IsMechanicTrackerBot(botAI, bot, SSC_MAP_ID, nullptr) ||
|
||||
GetDesignatedCoreLooter(group, botAI) == bot ||
|
||||
GetFirstTaintedCorePasser(group, botAI) == bot ||
|
||||
GetSecondTaintedCorePasser(group, botAI) == bot ||
|
||||
GetThirdTaintedCorePasser(group, botAI) == bot ||
|
||||
GetFourthTaintedCorePasser(group, botAI) == bot;
|
||||
}
|
||||
|
||||
bool LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger::IsActive()
|
||||
{
|
||||
return IsLadyVashjInPhase3(botAI);
|
||||
}
|
||||
|
||||
bool LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger::IsActive()
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "lady vashj"))
|
||||
return false;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->HasAura(SPELL_ENTANGLE))
|
||||
continue;
|
||||
|
||||
if (botAI->IsMelee(member))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
414
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h
Normal file
414
src/Ai/Raid/SerpentshrineCavern/Trigger/RaidSSCTriggers.h
Normal file
@@ -0,0 +1,414 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
|
||||
// General
|
||||
|
||||
class SerpentShrineCavernBotIsNotInCombatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
SerpentShrineCavernBotIsNotInCombatTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "serpent shrine cavern bot is not in combat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Trash
|
||||
|
||||
class UnderbogColossusSpawnedToxicPoolAfterDeathTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
UnderbogColossusSpawnedToxicPoolAfterDeathTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "underbog colossus spawned toxic pool after death") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GreyheartTidecallerWaterElementalTotemSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GreyheartTidecallerWaterElementalTotemSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "greyheart tidecaller water elemental totem spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
class HydrossTheUnstableBotIsFrostTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableBotIsFrostTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is frost tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableBotIsNatureTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableBotIsNatureTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable bot is nature tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableElementalsSpawnedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableElementalsSpawnedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable elementals spawned") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableDangerFromWaterTombsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableDangerFromWaterTombsTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable danger from water tombs") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableTankNeedsAggroUponPhaseChangeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable tank needs aggro upon phase change") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableAggroResetsUponPhaseChangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableAggroResetsUponPhaseChangeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable aggro resets upon phase change") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class HydrossTheUnstableNeedToManageTimersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HydrossTheUnstableNeedToManageTimersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hydross the unstable need to manage timers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
class TheLurkerBelowSpoutIsActiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowSpoutIsActiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below spout is active") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowBossIsActiveForMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowBossIsActiveForMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is active for main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowBossCastsGeyserTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowBossCastsGeyserTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss casts geyser") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowBossIsSubmergedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowBossIsSubmergedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below boss is submerged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class TheLurkerBelowNeedToPrepareTimerForSpoutTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
TheLurkerBelowNeedToPrepareTimerForSpoutTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "the lurker below need to prepare timer for spout") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
class LeotherasTheBlindBossIsInactiveTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossIsInactiveTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss is inactive") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindOnlyWarlockShouldTankDemonFormTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind only warlock should tank demon form") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossTransformedIntoDemonFormTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossTransformedIntoDemonFormTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss transformed into demon form") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossEngagedByRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossEngagedByRangedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss engaged by ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossChannelingWhirlwindTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossChannelingWhirlwindTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss channeling whirlwind") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBotHasTooManyChaosBlastStacksTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind bot has too many chaos blast stacks") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindInnerDemonHasAwakenedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindInnerDemonHasAwakenedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind inner demon has awakened") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindEnteredFinalPhaseTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindEnteredFinalPhaseTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind entered final phase") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindDemonFormTankNeedsAggro : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindDemonFormTankNeedsAggro(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind demon form tank needs aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LeotherasTheBlindBossWipesAggroUponPhaseChangeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "leotheras the blind boss wipes aggro upon phase change") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
class FathomLordKarathressBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressCaribdisEngagedByFirstAssistTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis engaged by first assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressSharkkisEngagedBySecondAssistTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress sharkkis engaged by second assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressTidalvessEngagedByThirdAssistTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tidalvess engaged by third assist tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressCaribdisTankNeedsDedicatedHealerTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress caribdis tank needs dedicated healer") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressPullingBossesTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressPullingBossesTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress pulling bosses") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressDeterminingKillOrderTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressDeterminingKillOrderTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress determining kill order") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class FathomLordKarathressTanksNeedToEstablishAggroTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
FathomLordKarathressTanksNeedToEstablishAggroTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "fathom-lord karathress tanks need to establish aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
class MorogrimTidewalkerPullingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerPullingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker pulling boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class MorogrimTidewalkerWaterGlobulesAreIncomingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
MorogrimTidewalkerWaterGlobulesAreIncomingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "morogrim tidewalker water globules are incoming") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
class LadyVashjBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjBossEngagedByRangedInPhase1Trigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBossEngagedByRangedInPhase1Trigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj boss engaged by ranged in phase 1") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjCastsShockBlastOnHighestAggroTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjCastsShockBlastOnHighestAggroTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj casts shock blast on highest aggro") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjBotHasStaticChargeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBotHasStaticChargeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot has static charge") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjPullingBossInPhase1AndPhase3Trigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjPullingBossInPhase1AndPhase3Trigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj pulling boss in phase 1 and phase 3") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjAddsSpawnInPhase2AndPhase3Trigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjAddsSpawnInPhase2AndPhase3Trigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj adds spawn in phase 2 and phase 3") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjCoilfangStriderIsApproachingTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjCoilfangStriderIsApproachingTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj coilfang strider is approaching") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjTaintedElementalCheatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjTaintedElementalCheatTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted elemental cheat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjTaintedCoreWasLootedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjTaintedCoreWasLootedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core was looted") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjTaintedCoreIsUnusableTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjTaintedCoreIsUnusableTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj tainted core is unusable") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjNeedToResetCorePassingTrackersTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjNeedToResetCorePassingTrackersTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj need to reset core passing trackers") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjToxicSporebatsAreSpewingPoisonCloudsTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj toxic sporebats are spewing poison clouds") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LadyVashjBotIsEntangledInToxicSporesOrStaticChargeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "lady vashj bot is entangled in toxic spores or static charge") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
583
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp
Normal file
583
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
#include "RaidSSCHelpers.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Creature.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
namespace SerpentShrineCavernHelpers
|
||||
{
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
|
||||
const Position HYDROSS_FROST_TANK_POSITION = { -236.669f, -358.352f, -0.828f };
|
||||
const Position HYDROSS_NATURE_TANK_POSITION = { -225.471f, -327.790f, -3.682f };
|
||||
|
||||
std::unordered_map<uint32, time_t> hydrossFrostDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> hydrossNatureDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> hydrossChangeToFrostPhaseTimer;
|
||||
std::unordered_map<uint32, time_t> hydrossChangeToNaturePhaseTimer;
|
||||
|
||||
bool HasMarkOfHydrossAt100Percent(Player* bot)
|
||||
{
|
||||
return bot->HasAura(SPELL_MARK_OF_HYDROSS_100) ||
|
||||
bot->HasAura(SPELL_MARK_OF_HYDROSS_250) ||
|
||||
bot->HasAura(SPELL_MARK_OF_HYDROSS_500);
|
||||
}
|
||||
|
||||
bool HasNoMarkOfHydross(Player* bot)
|
||||
{
|
||||
return !bot->HasAura(SPELL_MARK_OF_HYDROSS_10) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_25) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_50) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_100) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_250) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_HYDROSS_500);
|
||||
}
|
||||
|
||||
bool HasMarkOfCorruptionAt100Percent(Player* bot)
|
||||
{
|
||||
return bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) ||
|
||||
bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) ||
|
||||
bot->HasAura(SPELL_MARK_OF_CORRUPTION_500);
|
||||
}
|
||||
|
||||
bool HasNoMarkOfCorruption(Player* bot)
|
||||
{
|
||||
return !bot->HasAura(SPELL_MARK_OF_CORRUPTION_10) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_25) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_50) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_100) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_250) &&
|
||||
!bot->HasAura(SPELL_MARK_OF_CORRUPTION_500);
|
||||
}
|
||||
|
||||
// The Lurker Below
|
||||
|
||||
const Position LURKER_MAIN_TANK_POSITION = { 23.706f, -406.038f, -19.686f };
|
||||
|
||||
std::unordered_map<uint32, time_t> lurkerSpoutTimer;
|
||||
std::unordered_map<ObjectGuid, Position> lurkerRangedPositions;
|
||||
|
||||
bool IsLurkerCastingSpout(Unit* lurker)
|
||||
{
|
||||
if (!lurker || !lurker->HasUnitState(UNIT_STATE_CASTING))
|
||||
return false;
|
||||
|
||||
Spell* currentSpell = lurker->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (!currentSpell)
|
||||
return false;
|
||||
|
||||
uint32 spellId = currentSpell->m_spellInfo->Id;
|
||||
bool isSpout = spellId == SPELL_SPOUT_VISUAL;
|
||||
|
||||
return isSpout;
|
||||
}
|
||||
|
||||
// Leotheras the Blind
|
||||
|
||||
std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||
std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||
|
||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||
unit->IsInCombat() && !unit->HasAura(SPELL_METAMORPHOSIS))
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_LEOTHERAS_THE_BLIND &&
|
||||
unit->HasAura(SPELL_METAMORPHOSIS))
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& guid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(guid);
|
||||
if (unit && unit->GetEntry() == NPC_SHADOW_OF_LEOTHERAS)
|
||||
return unit;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* phase2 = GetPhase2LeotherasDemon(botAI);
|
||||
Unit* phase3 = GetPhase3LeotherasDemon(botAI);
|
||||
return phase2 ? phase2 : phase3;
|
||||
}
|
||||
|
||||
Player* GetLeotherasDemonFormTank(Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
// (1) First loop: Return the first assistant Warlock (real player or bot)
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_WARLOCK)
|
||||
continue;
|
||||
|
||||
if (group->IsAssistant(member->GetGUID()))
|
||||
return member;
|
||||
}
|
||||
|
||||
// (2) Fall back to first found bot Warlock
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
member->getClass() != CLASS_WARLOCK)
|
||||
continue;
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
// (3) Return nullptr if none found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
|
||||
const Position KARATHRESS_TANK_POSITION = { 474.403f, -531.118f, -7.548f };
|
||||
const Position TIDALVESS_TANK_POSITION = { 511.282f, -501.162f, -13.158f };
|
||||
const Position SHARKKIS_TANK_POSITION = { 508.057f, -541.109f, -10.133f };
|
||||
const Position CARIBDIS_TANK_POSITION = { 464.462f, -475.820f, -13.158f };
|
||||
const Position CARIBDIS_HEALER_POSITION = { 466.203f, -503.201f, -13.158f };
|
||||
const Position CARIBDIS_RANGED_DPS_POSITION = { 463.197f, -501.190f, -13.158f };
|
||||
|
||||
std::unordered_map<uint32, time_t> karathressDpsWaitTimer;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
|
||||
const Position TIDEWALKER_PHASE_1_TANK_POSITION = { 410.925f, -741.916f, -7.146f };
|
||||
const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT = { 407.035f, -759.479f, -7.168f };
|
||||
const Position TIDEWALKER_PHASE_2_TANK_POSITION = { 446.571f, -767.155f, -7.144f };
|
||||
const Position TIDEWALKER_PHASE_2_RANGED_POSITION = { 432.595f, -766.288f, -7.145f };
|
||||
|
||||
std::unordered_map<ObjectGuid, uint8> tidewalkerTankStep;
|
||||
std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
|
||||
const Position VASHJ_PLATFORM_CENTER_POSITION = { 29.634f, -923.541f, 42.985f };
|
||||
|
||||
std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||
std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||
std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||
std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||
std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||
std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||
|
||||
bool IsMainTankInSameSubgroup(Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group || !group->isRaidGroup())
|
||||
return false;
|
||||
|
||||
uint8 botSubGroup = group->GetMemberGroup(bot->GetGUID());
|
||||
if (botSubGroup >= MAX_RAID_SUBGROUPS)
|
||||
return false;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || member == bot || !member->IsAlive())
|
||||
continue;
|
||||
|
||||
if (group->GetMemberGroup(member->GetGUID()) != botSubGroup)
|
||||
continue;
|
||||
|
||||
if (PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member))
|
||||
{
|
||||
if (memberAI->IsMainTank(member))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsLadyVashjInPhase1(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
Creature* vashjCreature = vashj->ToCreature();
|
||||
return vashjCreature && vashjCreature->GetHealthPct() > 70.0f &&
|
||||
vashjCreature->GetReactState() != REACT_PASSIVE;
|
||||
}
|
||||
|
||||
bool IsLadyVashjInPhase2(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
Creature* vashjCreature = vashj->ToCreature();
|
||||
return vashjCreature && vashjCreature->GetReactState() == REACT_PASSIVE;
|
||||
}
|
||||
|
||||
bool IsLadyVashjInPhase3(PlayerbotAI* botAI)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
Creature* vashjCreature = vashj->ToCreature();
|
||||
return vashjCreature && vashjCreature->GetHealthPct() <= 50.0f &&
|
||||
vashjCreature->GetReactState() != REACT_PASSIVE;
|
||||
}
|
||||
|
||||
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!unit || !unit->IsAlive())
|
||||
return false;
|
||||
|
||||
uint32 entry = unit->GetEntry();
|
||||
|
||||
if (IsLadyVashjInPhase2(botAI))
|
||||
{
|
||||
return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL ||
|
||||
entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER;
|
||||
}
|
||||
else if (IsLadyVashjInPhase3(botAI))
|
||||
{
|
||||
return entry == NPC_TAINTED_ELEMENTAL || entry == NPC_ENCHANTED_ELEMENTAL ||
|
||||
entry == NPC_COILFANG_ELITE || entry == NPC_COILFANG_STRIDER ||
|
||||
entry == NPC_TOXIC_SPOREBAT || entry == NPC_LADY_VASHJ;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds)
|
||||
{
|
||||
Unit* vashj =
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "lady vashj")->Get();
|
||||
if (!vashj)
|
||||
return false;
|
||||
|
||||
if (group)
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->HasItemCount(ITEM_TAINTED_CORE, 1, false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32 instanceId = vashj->GetMap()->GetInstanceId();
|
||||
const time_t now = std::time(nullptr);
|
||||
|
||||
auto it = lastCoreInInventoryTime.find(instanceId);
|
||||
if (it != lastCoreInInventoryTime.end())
|
||||
{
|
||||
if ((now - it->second) <= static_cast<time_t>(graceSeconds))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* leader = nullptr;
|
||||
ObjectGuid leaderGuid = group->GetLeaderGUID();
|
||||
if (!leaderGuid.IsEmpty())
|
||||
leader = ObjectAccessor::FindPlayer(leaderGuid);
|
||||
|
||||
if (!botAI->HasCheat(BotCheatMask::raid))
|
||||
return leader;
|
||||
|
||||
Player* fallback = leader;
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == leader)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsMelee(member) && memberAI->IsDps(member))
|
||||
return member;
|
||||
|
||||
if (!fallback && memberAI->IsRangedDps(member))
|
||||
fallback = member;
|
||||
}
|
||||
|
||||
return fallback ? fallback : leader;
|
||||
}
|
||||
|
||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 0, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||
member == firstCorePasser)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 1, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistHealOfIndex(member, 2, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI)
|
||||
{
|
||||
if (!group)
|
||||
return nullptr;
|
||||
|
||||
Player* designatedLooter = GetDesignatedCoreLooter(group, botAI);
|
||||
Player* firstCorePasser = GetFirstTaintedCorePasser(group, botAI);
|
||||
Player* secondCorePasser = GetSecondTaintedCorePasser(group, botAI);
|
||||
Player* thirdCorePasser = GetThirdTaintedCorePasser(group, botAI);
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser ||
|
||||
member == thirdCorePasser)
|
||||
continue;
|
||||
|
||||
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
|
||||
if (!memberAI)
|
||||
continue;
|
||||
|
||||
if (memberAI->IsAssistRangedDpsOfIndex(member, 0, true))
|
||||
return member;
|
||||
}
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) ||
|
||||
botAI->IsTank(member) || member == designatedLooter ||
|
||||
member == firstCorePasser || member == secondCorePasser ||
|
||||
member == thirdCorePasser)
|
||||
continue;
|
||||
return member;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS =
|
||||
{
|
||||
47482, // NW
|
||||
47483, // NE
|
||||
47484, // SE
|
||||
47485 // SW
|
||||
};
|
||||
|
||||
// Get the positions of all active Shield Generators by their database GUIDs
|
||||
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||
Map* map, const std::vector<uint32>& generatorDbGuids)
|
||||
{
|
||||
std::vector<GeneratorInfo> generators;
|
||||
if (!map)
|
||||
return generators;
|
||||
|
||||
for (uint32 dbGuid : generatorDbGuids)
|
||||
{
|
||||
auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid);
|
||||
if (bounds.first == bounds.second)
|
||||
continue;
|
||||
|
||||
GameObject* go = bounds.first->second;
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
if (go->GetGoState() != GO_STATE_READY)
|
||||
continue;
|
||||
|
||||
GeneratorInfo info;
|
||||
info.guid = go->GetGUID();
|
||||
info.x = go->GetPositionX();
|
||||
info.y = go->GetPositionY();
|
||||
info.z = go->GetPositionZ();
|
||||
generators.push_back(info);
|
||||
}
|
||||
|
||||
return generators;
|
||||
}
|
||||
|
||||
// Returns the nearest active Shield Generator to the bot
|
||||
// Active generators are powered by NPC_WORLD_INVISIBLE_TRIGGER creatures,
|
||||
// which depawn after use
|
||||
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference)
|
||||
{
|
||||
if (!reference)
|
||||
return nullptr;
|
||||
|
||||
std::list<Creature*> triggers;
|
||||
constexpr float searchRange = 150.0f;
|
||||
reference->GetCreatureListWithEntryInGrid(
|
||||
triggers, NPC_WORLD_INVISIBLE_TRIGGER, searchRange);
|
||||
|
||||
Creature* nearest = nullptr;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
|
||||
for (Creature* creature : triggers)
|
||||
{
|
||||
if (!creature->IsAlive())
|
||||
continue;
|
||||
|
||||
float dist = reference->GetDistance(creature);
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearest = creature;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
||||
const GeneratorInfo* GetNearestGeneratorToBot(
|
||||
Player* bot, const std::vector<GeneratorInfo>& generators)
|
||||
{
|
||||
if (!bot || generators.empty())
|
||||
return nullptr;
|
||||
|
||||
const GeneratorInfo* nearest = nullptr;
|
||||
float minDist = std::numeric_limits<float>::max();
|
||||
|
||||
for (auto const& gen : generators)
|
||||
{
|
||||
float dist = bot->GetExactDist(gen.x, gen.y, gen.z);
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
nearest = &gen;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
189
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h
Normal file
189
src/Ai/Raid/SerpentshrineCavern/Util/RaidSSCHelpers.h
Normal file
@@ -0,0 +1,189 @@
|
||||
#ifndef _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDSSCHELPERS_H_
|
||||
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Position.h"
|
||||
#include "Unit.h"
|
||||
|
||||
namespace SerpentShrineCavernHelpers
|
||||
{
|
||||
enum SerpentShrineCavernSpells
|
||||
{
|
||||
// Trash Mobs
|
||||
SPELL_TOXIC_POOL = 38718,
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
SPELL_MARK_OF_HYDROSS_10 = 38215,
|
||||
SPELL_MARK_OF_HYDROSS_25 = 38216,
|
||||
SPELL_MARK_OF_HYDROSS_50 = 38217,
|
||||
SPELL_MARK_OF_HYDROSS_100 = 38218,
|
||||
SPELL_MARK_OF_HYDROSS_250 = 38231,
|
||||
SPELL_MARK_OF_HYDROSS_500 = 40584,
|
||||
SPELL_MARK_OF_CORRUPTION_10 = 38219,
|
||||
SPELL_MARK_OF_CORRUPTION_25 = 38220,
|
||||
SPELL_MARK_OF_CORRUPTION_50 = 38221,
|
||||
SPELL_MARK_OF_CORRUPTION_100 = 38222,
|
||||
SPELL_MARK_OF_CORRUPTION_250 = 38230,
|
||||
SPELL_MARK_OF_CORRUPTION_500 = 40583,
|
||||
SPELL_CORRUPTION = 37961,
|
||||
|
||||
// The Lurker Below
|
||||
SPELL_SPOUT_VISUAL = 37431,
|
||||
|
||||
// Leotheras the Blind
|
||||
SPELL_LEOTHERAS_BANISHED = 37546,
|
||||
SPELL_WHIRLWIND = 37640,
|
||||
SPELL_WHIRLWIND_CHANNEL = 37641,
|
||||
SPELL_METAMORPHOSIS = 37673,
|
||||
SPELL_CHAOS_BLAST = 37675,
|
||||
SPELL_INSIDIOUS_WHISPER = 37676,
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
SPELL_FEAR_WARD = 6346,
|
||||
SPELL_POISON_BOLT = 38253,
|
||||
SPELL_STATIC_CHARGE = 38280,
|
||||
SPELL_ENTANGLE = 38316,
|
||||
|
||||
// Druid
|
||||
SPELL_CAT_FORM = 768,
|
||||
SPELL_BEAR_FORM = 5487,
|
||||
SPELL_DIRE_BEAR_FORM = 9634,
|
||||
SPELL_TREE_OF_LIFE = 33891,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Mage
|
||||
SPELL_SLOW = 31589,
|
||||
|
||||
// Shaman
|
||||
SPELL_GROUNDING_TOTEM_EFFECT = 8178,
|
||||
|
||||
// Warlock
|
||||
SPELL_CURSE_OF_EXHAUSTION = 18223,
|
||||
|
||||
// Item
|
||||
SPELL_HEAVY_NETHERWEAVE_NET = 31368,
|
||||
};
|
||||
|
||||
enum SerpentShrineCavernNPCs
|
||||
{
|
||||
// Trash Mobs
|
||||
NPC_WATER_ELEMENTAL_TOTEM = 22236,
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
NPC_PURE_SPAWN_OF_HYDROSS = 22035,
|
||||
NPC_TAINTED_SPAWN_OF_HYDROSS = 22036,
|
||||
|
||||
// The Lurker Below
|
||||
NPC_COILFANG_GUARDIAN = 21873,
|
||||
|
||||
// Leotheras the Blind
|
||||
NPC_LEOTHERAS_THE_BLIND = 21215,
|
||||
NPC_GREYHEART_SPELLBINDER = 21806,
|
||||
NPC_INNER_DEMON = 21857,
|
||||
NPC_SHADOW_OF_LEOTHERAS = 21875,
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
NPC_SPITFIRE_TOTEM = 22091,
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
NPC_WORLD_INVISIBLE_TRIGGER = 12999,
|
||||
NPC_LADY_VASHJ = 21212,
|
||||
NPC_ENCHANTED_ELEMENTAL = 21958,
|
||||
NPC_TAINTED_ELEMENTAL = 22009,
|
||||
NPC_COILFANG_ELITE = 22055,
|
||||
NPC_COILFANG_STRIDER = 22056,
|
||||
NPC_TOXIC_SPOREBAT = 22140,
|
||||
NPC_SPORE_DROP_TRIGGER = 22207,
|
||||
};
|
||||
|
||||
enum SerpentShrineCavernItems
|
||||
{
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
ITEM_TAINTED_CORE = 31088,
|
||||
|
||||
// Tailoring
|
||||
ITEM_HEAVY_NETHERWEAVE_NET = 24269,
|
||||
};
|
||||
|
||||
constexpr uint32 SSC_MAP_ID = 548;
|
||||
|
||||
// Hydross the Unstable <Duke of Currents>
|
||||
extern const Position HYDROSS_FROST_TANK_POSITION;
|
||||
extern const Position HYDROSS_NATURE_TANK_POSITION;
|
||||
extern std::unordered_map<uint32, time_t> hydrossFrostDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> hydrossNatureDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> hydrossChangeToFrostPhaseTimer;
|
||||
extern std::unordered_map<uint32, time_t> hydrossChangeToNaturePhaseTimer;
|
||||
bool HasMarkOfHydrossAt100Percent(Player* bot);
|
||||
bool HasNoMarkOfHydross(Player* bot);
|
||||
bool HasMarkOfCorruptionAt100Percent(Player* bot);
|
||||
bool HasNoMarkOfCorruption(Player* bot);
|
||||
|
||||
// The Lurker Below
|
||||
extern const Position LURKER_MAIN_TANK_POSITION;
|
||||
extern std::unordered_map<uint32, time_t> lurkerSpoutTimer;
|
||||
extern std::unordered_map<ObjectGuid, Position> lurkerRangedPositions;
|
||||
bool IsLurkerCastingSpout(Unit* lurker);
|
||||
|
||||
// Leotheras the Blind
|
||||
extern std::unordered_map<uint32, time_t> leotherasHumanFormDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> leotherasDemonFormDpsWaitTimer;
|
||||
extern std::unordered_map<uint32, time_t> leotherasFinalPhaseDpsWaitTimer;
|
||||
Unit* GetLeotherasHuman(PlayerbotAI* botAI);
|
||||
Unit* GetPhase2LeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetPhase3LeotherasDemon(PlayerbotAI* botAI);
|
||||
Unit* GetActiveLeotherasDemon(PlayerbotAI* botAI);
|
||||
Player* GetLeotherasDemonFormTank(Player* bot);
|
||||
|
||||
// Fathom-Lord Karathress
|
||||
extern const Position KARATHRESS_TANK_POSITION;
|
||||
extern const Position TIDALVESS_TANK_POSITION;
|
||||
extern const Position SHARKKIS_TANK_POSITION;
|
||||
extern const Position CARIBDIS_TANK_POSITION;
|
||||
extern const Position CARIBDIS_HEALER_POSITION;
|
||||
extern const Position CARIBDIS_RANGED_DPS_POSITION;
|
||||
extern std::unordered_map<uint32, time_t> karathressDpsWaitTimer;
|
||||
|
||||
// Morogrim Tidewalker
|
||||
extern const Position TIDEWALKER_PHASE_1_TANK_POSITION;
|
||||
extern const Position TIDEWALKER_PHASE_TRANSITION_WAYPOINT;
|
||||
extern const Position TIDEWALKER_PHASE_2_TANK_POSITION;
|
||||
extern const Position TIDEWALKER_PHASE_2_RANGED_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, uint8> tidewalkerTankStep;
|
||||
extern std::unordered_map<ObjectGuid, uint8> tidewalkerRangedStep;
|
||||
|
||||
// Lady Vashj <Coilfang Matron>
|
||||
constexpr float VASHJ_PLATFORM_Z = 42.985f;
|
||||
extern const Position VASHJ_PLATFORM_CENTER_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, Position> vashjRangedPositions;
|
||||
extern std::unordered_map<ObjectGuid, bool> hasReachedVashjRangedPosition;
|
||||
extern std::unordered_map<uint32, ObjectGuid> nearestTriggerGuid;
|
||||
extern std::unordered_map<ObjectGuid, Position> intendedLineup;
|
||||
extern std::unordered_map<uint32, time_t> lastImbueAttempt;
|
||||
extern std::unordered_map<uint32, time_t> lastCoreInInventoryTime;
|
||||
bool IsMainTankInSameSubgroup(Player* bot);
|
||||
bool IsLadyVashjInPhase1(PlayerbotAI* botAI);
|
||||
bool IsLadyVashjInPhase2(PlayerbotAI* botAI);
|
||||
bool IsLadyVashjInPhase3(PlayerbotAI* botAI);
|
||||
bool IsValidLadyVashjCombatNpc(Unit* unit, PlayerbotAI* botAI);
|
||||
bool AnyRecentCoreInInventory(Group* group, PlayerbotAI* botAI, uint32 graceSeconds = 3);
|
||||
Player* GetDesignatedCoreLooter(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetFirstTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetSecondTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetThirdTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
Player* GetFourthTaintedCorePasser(Group* group, PlayerbotAI* botAI);
|
||||
struct GeneratorInfo { ObjectGuid guid; float x, y, z; };
|
||||
extern const std::vector<uint32> SHIELD_GENERATOR_DB_GUIDS;
|
||||
std::vector<GeneratorInfo> GetAllGeneratorInfosByDbGuids(
|
||||
Map* map, const std::vector<uint32>& generatorDbGuids);
|
||||
Unit* GetNearestActiveShieldGeneratorTriggerByEntry(Unit* reference);
|
||||
const GeneratorInfo* GetNearestGeneratorToBot(
|
||||
Player* bot, const std::vector<GeneratorInfo>& generators);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "GameObject.h"
|
||||
#include "Group.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
@@ -19,11 +18,9 @@
|
||||
#include "Position.h"
|
||||
#include "RaidUlduarBossHelper.h"
|
||||
#include "RaidUlduarScripts.h"
|
||||
#include "RaidUlduarStrategy.h"
|
||||
#include "RtiValue.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Unit.h"
|
||||
#include "Vehicle.h"
|
||||
#include <RtiTargetValue.h>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#include "RaidUlduarMultipliers.h"
|
||||
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "PriestActions.h"
|
||||
#include "RaidUlduarActions.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "UseMeetingStoneAction.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
float FlameLeviathanMultiplier::GetValue(Action* action)
|
||||
{
|
||||
// if (dynamic_cast<FleeAction*>(action))
|
||||
// return 0.0f;
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_
|
||||
#define _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarBossHelper.h"
|
||||
|
||||
class FlameLeviathanMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FlameLeviathanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flame leviathan") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,169 +0,0 @@
|
||||
#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "EventMap.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
const uint32 ULDUAR_MAP_ID = 603;
|
||||
|
||||
class RazorscaleBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
// Enums and constants specific to Razorscale
|
||||
enum RazorscaleUnits : uint32
|
||||
{
|
||||
UNIT_RAZORSCALE = 33186,
|
||||
UNIT_DARK_RUNE_SENTINEL = 33846,
|
||||
UNIT_DARK_RUNE_WATCHER = 33453,
|
||||
UNIT_DARK_RUNE_GUARDIAN = 33388,
|
||||
UNIT_DEVOURING_FLAME = 34188,
|
||||
};
|
||||
|
||||
enum RazorscaleGameObjects : uint32
|
||||
{
|
||||
GO_RAZORSCALE_HARPOON_1 = 194519,
|
||||
GO_RAZORSCALE_HARPOON_2 = 194541,
|
||||
GO_RAZORSCALE_HARPOON_3 = 194542,
|
||||
GO_RAZORSCALE_HARPOON_4 = 194543,
|
||||
};
|
||||
|
||||
enum RazorscaleSpells : uint32
|
||||
{
|
||||
SPELL_CHAIN_1 = 49679,
|
||||
SPELL_CHAIN_2 = 49682,
|
||||
SPELL_CHAIN_3 = 49683,
|
||||
SPELL_CHAIN_4 = 49684,
|
||||
SPELL_SENTINEL_WHIRLWIND = 63806,
|
||||
SPELL_STUN_AURA = 62794,
|
||||
SPELL_FUSEARMOR = 64771
|
||||
};
|
||||
|
||||
static constexpr uint32 FUSEARMOR_THRESHOLD = 2;
|
||||
|
||||
// Constants for arena parameters
|
||||
static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f;
|
||||
static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f;
|
||||
|
||||
// Harpoon cooldown (seconds)
|
||||
static constexpr time_t HARPOON_COOLDOWN_DURATION = 5;
|
||||
|
||||
// Structure for harpoon data
|
||||
struct HarpoonData
|
||||
{
|
||||
uint32 gameObjectEntry;
|
||||
uint32 chainSpellId;
|
||||
};
|
||||
|
||||
explicit RazorscaleBossHelper(PlayerbotAI* botAI)
|
||||
: AiObject(botAI), _boss(nullptr) {}
|
||||
|
||||
bool UpdateBossAI();
|
||||
Unit* GetBoss() const;
|
||||
|
||||
bool IsGroundPhase() const;
|
||||
bool IsFlyingPhase() const;
|
||||
|
||||
bool IsHarpoonFired(uint32 chainSpellId) const;
|
||||
static bool IsHarpoonReady(GameObject* harpoonGO);
|
||||
static void SetHarpoonOnCooldown(GameObject* harpoonGO);
|
||||
GameObject* FindNearestHarpoon(float x, float y, float z) const;
|
||||
|
||||
static const std::vector<HarpoonData>& GetHarpoonData();
|
||||
|
||||
void AssignRolesBasedOnHealth();
|
||||
bool AreRolesAssigned() const;
|
||||
bool CanSwapRoles() const;
|
||||
|
||||
private:
|
||||
Unit* _boss;
|
||||
|
||||
// A map to track the last role swap *per bot* by their GUID
|
||||
static std::unordered_map<ObjectGuid, std::time_t> _lastRoleSwapTime;
|
||||
|
||||
// The cooldown that applies to every bot
|
||||
static const std::time_t _roleSwapCooldown = 10;
|
||||
|
||||
static std::unordered_map<ObjectGuid, time_t> _harpoonCooldowns;
|
||||
};
|
||||
|
||||
// template <class BossAiType>
|
||||
// class GenericBossHelper : public AiObject
|
||||
// {
|
||||
// public:
|
||||
// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
||||
// virtual bool UpdateBossAI()
|
||||
// {
|
||||
// if (!bot->IsInCombat())
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (!_unit)
|
||||
// {
|
||||
// _unit = AI_VALUE2(Unit*, "find target", _name);
|
||||
// if (!_unit)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _target = _unit->ToCreature();
|
||||
// if (!_target)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
||||
// if (!_ai)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _event_map = &_ai->events;
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _timer = _event_map->GetTimer();
|
||||
// return true;
|
||||
// }
|
||||
// virtual void Reset()
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// _target = nullptr;
|
||||
// _ai = nullptr;
|
||||
// _event_map = nullptr;
|
||||
// _timer = 0;
|
||||
// }
|
||||
|
||||
// protected:
|
||||
// std::string _name;
|
||||
// Unit* _unit = nullptr;
|
||||
// Creature* _target = nullptr;
|
||||
// BossAiType* _ai = nullptr;
|
||||
// EventMap* _event_map = nullptr;
|
||||
// uint32 _timer = 0;
|
||||
// };
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "RaidUlduarStrategy.h"
|
||||
|
||||
#include "RaidUlduarMultipliers.h"
|
||||
|
||||
void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
//
|
||||
@@ -316,8 +314,3 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"yogg-saron phase 3 positioning trigger",
|
||||
{ NextAction("yogg-saron phase 3 positioning action", ACTION_RAID) }));
|
||||
}
|
||||
|
||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new FlameLeviathanMultiplier(botAI));
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
#define _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidUlduarStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "uld"; }
|
||||
virtual std::string const getName() override { return "ulduar"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1634,7 +1634,7 @@ bool VezaxShadowCrashTrigger::IsActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
return botAI->HasAura(SPELL_SHADOW_CRASH, bot);
|
||||
return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot);
|
||||
}
|
||||
|
||||
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
||||
|
||||
@@ -3,187 +3,9 @@
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "RaidUlduarBossHelper.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
enum UlduarIDs
|
||||
{
|
||||
// Iron Assembly
|
||||
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
||||
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
||||
SPELL_OVERLOAD_10_MAN = 61869,
|
||||
SPELL_OVERLOAD_25_MAN = 63481,
|
||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||
SPELL_RUNE_OF_POWER = 64320,
|
||||
|
||||
// Kologarn
|
||||
NPC_RIGHT_ARM = 32934,
|
||||
NPC_RUBBLE = 33768,
|
||||
SPELL_CRUNCH_ARMOR = 64002,
|
||||
|
||||
SPELL_FOCUSED_EYEBEAM_10_2 = 63346,
|
||||
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
||||
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
||||
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
||||
|
||||
// Hodir
|
||||
NPC_SNOWPACKED_ICICLE = 33174,
|
||||
NPC_TOASTY_FIRE = 33342,
|
||||
SPELL_FLASH_FREEZE = 61968,
|
||||
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
||||
|
||||
// Freya
|
||||
NPC_SNAPLASHER = 32916,
|
||||
NPC_STORM_LASHER = 32919,
|
||||
NPC_DETONATING_LASHER = 32918,
|
||||
NPC_ANCIENT_WATER_SPIRIT = 33202,
|
||||
NPC_ANCIENT_CONSERVATOR = 33203,
|
||||
NPC_HEALTHY_SPORE = 33215,
|
||||
NPC_EONARS_GIFT = 33228,
|
||||
GOBJECT_NATURE_BOMB = 194902,
|
||||
|
||||
// Thorim
|
||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
||||
NPC_JORMUNGAR_BEHEMOT = 32882,
|
||||
NPC_DARK_RUNE_WARBRINGER = 32877,
|
||||
NPC_DARK_RUNE_EVOKER = 32878,
|
||||
NPC_DARK_RUNE_CHAMPION = 32876,
|
||||
NPC_DARK_RUNE_COMMONER = 32904,
|
||||
NPC_IRON_RING_GUARD = 32874,
|
||||
NPC_RUNIC_COLOSSUS = 32872,
|
||||
NPC_ANCIENT_RUNE_GIANT = 32873,
|
||||
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
||||
NPC_IRON_HONOR_GUARD = 32875,
|
||||
SPELL_UNBALANCING_STRIKE = 62130,
|
||||
|
||||
// Mimiron
|
||||
NPC_LEVIATHAN_MKII = 33432,
|
||||
NPC_VX001 = 33651,
|
||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||
NPC_BOMB_BOT = 33836,
|
||||
NPC_ROCKET_STRIKE_N = 34047,
|
||||
NPC_ASSAULT_BOT = 34057,
|
||||
NPC_PROXIMITY_MINE = 34362,
|
||||
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
||||
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
||||
SPELL_SPINNING_UP = 63414,
|
||||
SPELL_SHOCK_BLAST = 63631,
|
||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||
|
||||
// General Vezax
|
||||
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||
SPELL_SHADOW_CRASH = 63277,
|
||||
|
||||
// Yogg-Saron
|
||||
ACTION_ILLUSION_DRAGONS = 1,
|
||||
ACTION_ILLUSION_ICECROWN = 2,
|
||||
ACTION_ILLUSION_STORMWIND = 3,
|
||||
NPC_GUARDIAN_OF_YS = 33136,
|
||||
NPC_YOGG_SARON = 33288,
|
||||
NPC_OMINOUS_CLOUD = 33292,
|
||||
NPC_RUBY_CONSORT = 33716,
|
||||
NPC_AZURE_CONSORT = 33717,
|
||||
NPC_BRONZE_CONSORT = 33718,
|
||||
NPC_EMERALD_CONSORT = 33719,
|
||||
NPC_OBSIDIAN_CONSORT = 33720,
|
||||
NPC_ALEXTRASZA = 33536,
|
||||
NPC_MALYGOS_ILLUSION = 33535,
|
||||
NPC_NELTHARION = 33523,
|
||||
NPC_YSERA = 33495,
|
||||
GO_DRAGON_SOUL = 194462,
|
||||
NPC_SARA_PHASE_1 = 33134,
|
||||
NPC_LICH_KING_ILLUSION = 33441,
|
||||
NPC_IMMOLATED_CHAMPION = 33442,
|
||||
NPC_SUIT_OF_ARMOR = 33433,
|
||||
NPC_GARONA = 33436,
|
||||
NPC_KING_LLANE = 33437,
|
||||
NPC_DEATHSWORN_ZEALOT = 33567,
|
||||
NPC_INFLUENCE_TENTACLE = 33943,
|
||||
NPC_DEATH_ORB = 33882,
|
||||
NPC_BRAIN = 33890,
|
||||
NPC_CRUSHER_TENTACLE = 33966,
|
||||
NPC_CONSTRICTOR_TENTACLE = 33983,
|
||||
NPC_CORRUPTOR_TENTACLE = 33985,
|
||||
NPC_IMMORTAL_GUARDIAN = 33988,
|
||||
NPC_LAUGHING_SKULL = 33990,
|
||||
NPC_SANITY_WELL = 33991,
|
||||
NPC_DESCEND_INTO_MADNESS = 34072,
|
||||
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
||||
SPELL_SANITY = 63050,
|
||||
SPELL_BRAIN_LINK = 63802,
|
||||
SPELL_MALADY_OF_THE_MIND = 63830,
|
||||
SPELL_SHADOW_BARRIER = 63894,
|
||||
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
||||
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
||||
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
||||
SPELL_TELEPORT_BACK = 63992,
|
||||
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
||||
SPELL_INDUCE_MADNESS = 64059,
|
||||
SPELL_LUNATIC_GAZE_YS = 64163,
|
||||
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
||||
|
||||
// Buffs
|
||||
SPELL_FROST_TRAP = 13809
|
||||
};
|
||||
|
||||
const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||
const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||
const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||
const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
const float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
||||
const float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
||||
const float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
||||
const float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
||||
|
||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
||||
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
||||
|
||||
//
|
||||
// Flame Levi
|
||||
//
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "ChatHelper.h"
|
||||
#include "RaidUlduarBossHelper.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "GameObject.h"
|
||||
@@ -9,6 +8,44 @@
|
||||
#include "Playerbots.h"
|
||||
#include "World.h"
|
||||
|
||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
||||
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
||||
|
||||
// Prevent harpoon spam
|
||||
std::unordered_map<ObjectGuid, time_t> RazorscaleBossHelper::_harpoonCooldowns;
|
||||
// Prevent role assignment spam
|
||||
341
src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h
Normal file
341
src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "EventMap.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptedCreature.h"
|
||||
|
||||
constexpr uint32 ULDUAR_MAP_ID = 603;
|
||||
|
||||
enum UlduarIDs
|
||||
{
|
||||
// Iron Assembly
|
||||
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
||||
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
||||
SPELL_OVERLOAD_10_MAN = 61869,
|
||||
SPELL_OVERLOAD_25_MAN = 63481,
|
||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||
SPELL_RUNE_OF_POWER = 64320,
|
||||
|
||||
// Kologarn
|
||||
NPC_RIGHT_ARM = 32934,
|
||||
NPC_RUBBLE = 33768,
|
||||
SPELL_CRUNCH_ARMOR = 64002,
|
||||
|
||||
SPELL_FOCUSED_EYEBEAM_10_2 = 63346,
|
||||
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
||||
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
||||
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
||||
|
||||
// Hodir
|
||||
NPC_SNOWPACKED_ICICLE = 33174,
|
||||
NPC_TOASTY_FIRE = 33342,
|
||||
SPELL_FLASH_FREEZE = 61968,
|
||||
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
||||
|
||||
// Freya
|
||||
NPC_SNAPLASHER = 32916,
|
||||
NPC_STORM_LASHER = 32919,
|
||||
NPC_DETONATING_LASHER = 32918,
|
||||
NPC_ANCIENT_WATER_SPIRIT = 33202,
|
||||
NPC_ANCIENT_CONSERVATOR = 33203,
|
||||
NPC_HEALTHY_SPORE = 33215,
|
||||
NPC_EONARS_GIFT = 33228,
|
||||
GOBJECT_NATURE_BOMB = 194902,
|
||||
|
||||
// Thorim
|
||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
||||
NPC_JORMUNGAR_BEHEMOT = 32882,
|
||||
NPC_DARK_RUNE_WARBRINGER = 32877,
|
||||
NPC_DARK_RUNE_EVOKER = 32878,
|
||||
NPC_DARK_RUNE_CHAMPION = 32876,
|
||||
NPC_DARK_RUNE_COMMONER = 32904,
|
||||
NPC_IRON_RING_GUARD = 32874,
|
||||
NPC_RUNIC_COLOSSUS = 32872,
|
||||
NPC_ANCIENT_RUNE_GIANT = 32873,
|
||||
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
||||
NPC_IRON_HONOR_GUARD = 32875,
|
||||
SPELL_UNBALANCING_STRIKE = 62130,
|
||||
|
||||
// Mimiron
|
||||
NPC_LEVIATHAN_MKII = 33432,
|
||||
NPC_VX001 = 33651,
|
||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||
NPC_BOMB_BOT = 33836,
|
||||
NPC_ROCKET_STRIKE_N = 34047,
|
||||
NPC_ASSAULT_BOT = 34057,
|
||||
NPC_PROXIMITY_MINE = 34362,
|
||||
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
||||
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
||||
SPELL_SPINNING_UP = 63414,
|
||||
SPELL_SHOCK_BLAST = 63631,
|
||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||
|
||||
// General Vezax
|
||||
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||
SPELL_VEZAX_SHADOW_CRASH = 63277,
|
||||
|
||||
// Yogg-Saron
|
||||
ACTION_ILLUSION_DRAGONS = 1,
|
||||
ACTION_ILLUSION_ICECROWN = 2,
|
||||
ACTION_ILLUSION_STORMWIND = 3,
|
||||
NPC_GUARDIAN_OF_YS = 33136,
|
||||
NPC_YOGG_SARON = 33288,
|
||||
NPC_OMINOUS_CLOUD = 33292,
|
||||
NPC_RUBY_CONSORT = 33716,
|
||||
NPC_AZURE_CONSORT = 33717,
|
||||
NPC_BRONZE_CONSORT = 33718,
|
||||
NPC_EMERALD_CONSORT = 33719,
|
||||
NPC_OBSIDIAN_CONSORT = 33720,
|
||||
NPC_ALEXTRASZA = 33536,
|
||||
NPC_MALYGOS_ILLUSION = 33535,
|
||||
NPC_NELTHARION = 33523,
|
||||
NPC_YSERA = 33495,
|
||||
GO_DRAGON_SOUL = 194462,
|
||||
NPC_SARA_PHASE_1 = 33134,
|
||||
NPC_LICH_KING_ILLUSION = 33441,
|
||||
NPC_IMMOLATED_CHAMPION = 33442,
|
||||
NPC_SUIT_OF_ARMOR = 33433,
|
||||
NPC_GARONA = 33436,
|
||||
NPC_KING_LLANE = 33437,
|
||||
NPC_DEATHSWORN_ZEALOT = 33567,
|
||||
NPC_INFLUENCE_TENTACLE = 33943,
|
||||
NPC_DEATH_ORB = 33882,
|
||||
NPC_BRAIN = 33890,
|
||||
NPC_CRUSHER_TENTACLE = 33966,
|
||||
NPC_CONSTRICTOR_TENTACLE = 33983,
|
||||
NPC_CORRUPTOR_TENTACLE = 33985,
|
||||
NPC_IMMORTAL_GUARDIAN = 33988,
|
||||
NPC_LAUGHING_SKULL = 33990,
|
||||
NPC_SANITY_WELL = 33991,
|
||||
NPC_DESCEND_INTO_MADNESS = 34072,
|
||||
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
||||
SPELL_SANITY = 63050,
|
||||
SPELL_BRAIN_LINK = 63802,
|
||||
SPELL_MALADY_OF_THE_MIND = 63830,
|
||||
SPELL_SHADOW_BARRIER = 63894,
|
||||
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
||||
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
||||
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
||||
SPELL_TELEPORT_BACK = 63992,
|
||||
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
||||
SPELL_INDUCE_MADNESS = 64059,
|
||||
SPELL_LUNATIC_GAZE_YS = 64163,
|
||||
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
||||
|
||||
// Buffs
|
||||
SPELL_FROST_TRAP = 13809
|
||||
};
|
||||
|
||||
constexpr float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||
constexpr float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||
constexpr float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||
constexpr float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
constexpr float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
||||
|
||||
extern const Position ULDUAR_THORIM_NEAR_ARENA_CENTER;
|
||||
extern const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3;
|
||||
extern const Position ULDUAR_THORIM_JUMP_END_POINT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_TANK_SPOT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT;
|
||||
extern const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT;
|
||||
extern const Position ULDUAR_YOGG_SARON_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE;
|
||||
extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE;
|
||||
extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE;
|
||||
extern const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT;
|
||||
extern const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT;
|
||||
|
||||
class RazorscaleBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
// Enums and constants specific to Razorscale
|
||||
enum RazorscaleUnits : uint32
|
||||
{
|
||||
UNIT_RAZORSCALE = 33186,
|
||||
UNIT_DARK_RUNE_SENTINEL = 33846,
|
||||
UNIT_DARK_RUNE_WATCHER = 33453,
|
||||
UNIT_DARK_RUNE_GUARDIAN = 33388,
|
||||
UNIT_DEVOURING_FLAME = 34188,
|
||||
};
|
||||
|
||||
enum RazorscaleGameObjects : uint32
|
||||
{
|
||||
GO_RAZORSCALE_HARPOON_1 = 194519,
|
||||
GO_RAZORSCALE_HARPOON_2 = 194541,
|
||||
GO_RAZORSCALE_HARPOON_3 = 194542,
|
||||
GO_RAZORSCALE_HARPOON_4 = 194543,
|
||||
};
|
||||
|
||||
enum RazorscaleSpells : uint32
|
||||
{
|
||||
SPELL_CHAIN_1 = 49679,
|
||||
SPELL_CHAIN_2 = 49682,
|
||||
SPELL_CHAIN_3 = 49683,
|
||||
SPELL_CHAIN_4 = 49684,
|
||||
SPELL_SENTINEL_WHIRLWIND = 63806,
|
||||
SPELL_STUN_AURA = 62794,
|
||||
SPELL_FUSEARMOR = 64771
|
||||
};
|
||||
|
||||
static constexpr uint32 FUSEARMOR_THRESHOLD = 2;
|
||||
|
||||
// Constants for arena parameters
|
||||
static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f;
|
||||
static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f;
|
||||
|
||||
// Harpoon cooldown (seconds)
|
||||
static constexpr time_t HARPOON_COOLDOWN_DURATION = 5;
|
||||
|
||||
// Structure for harpoon data
|
||||
struct HarpoonData
|
||||
{
|
||||
uint32 gameObjectEntry;
|
||||
uint32 chainSpellId;
|
||||
};
|
||||
|
||||
explicit RazorscaleBossHelper(PlayerbotAI* botAI)
|
||||
: AiObject(botAI), _boss(nullptr) {}
|
||||
|
||||
bool UpdateBossAI();
|
||||
Unit* GetBoss() const;
|
||||
|
||||
bool IsGroundPhase() const;
|
||||
bool IsFlyingPhase() const;
|
||||
|
||||
bool IsHarpoonFired(uint32 chainSpellId) const;
|
||||
static bool IsHarpoonReady(GameObject* harpoonGO);
|
||||
static void SetHarpoonOnCooldown(GameObject* harpoonGO);
|
||||
GameObject* FindNearestHarpoon(float x, float y, float z) const;
|
||||
|
||||
static const std::vector<HarpoonData>& GetHarpoonData();
|
||||
|
||||
void AssignRolesBasedOnHealth();
|
||||
bool AreRolesAssigned() const;
|
||||
bool CanSwapRoles() const;
|
||||
|
||||
private:
|
||||
Unit* _boss;
|
||||
|
||||
// A map to track the last role swap *per bot* by their GUID
|
||||
static std::unordered_map<ObjectGuid, std::time_t> _lastRoleSwapTime;
|
||||
|
||||
// The cooldown that applies to every bot
|
||||
static const std::time_t _roleSwapCooldown = 10;
|
||||
|
||||
static std::unordered_map<ObjectGuid, time_t> _harpoonCooldowns;
|
||||
};
|
||||
|
||||
// template <class BossAiType>
|
||||
// class GenericBossHelper : public AiObject
|
||||
// {
|
||||
// public:
|
||||
// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
||||
// virtual bool UpdateBossAI()
|
||||
// {
|
||||
// if (!bot->IsInCombat())
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (!_unit)
|
||||
// {
|
||||
// _unit = AI_VALUE2(Unit*, "find target", _name);
|
||||
// if (!_unit)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _target = _unit->ToCreature();
|
||||
// if (!_target)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
||||
// if (!_ai)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _event_map = &_ai->events;
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _timer = _event_map->GetTimer();
|
||||
// return true;
|
||||
// }
|
||||
// virtual void Reset()
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// _target = nullptr;
|
||||
// _ai = nullptr;
|
||||
// _event_map = nullptr;
|
||||
// _timer = 0;
|
||||
// }
|
||||
|
||||
// protected:
|
||||
// std::string _name;
|
||||
// Unit* _unit = nullptr;
|
||||
// Creature* _target = nullptr;
|
||||
// BossAiType* _ai = nullptr;
|
||||
// EventMap* _event_map = nullptr;
|
||||
// uint32 _timer = 0;
|
||||
// };
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PLAYERBOT_RAIDVOAACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "BossAuraActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidVoAActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidVoATriggers.h"
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include "PaladinAiObjectContext.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PriestAiObjectContext.h"
|
||||
#include "RaidUlduarActionContext.h"
|
||||
#include "RaidUlduarTriggerContext.h"
|
||||
#include "RogueAiObjectContext.h"
|
||||
#include "ShamanAiObjectContext.h"
|
||||
#include "SharedValueContext.h"
|
||||
@@ -43,12 +41,16 @@
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
|
||||
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||
@@ -115,6 +117,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
||||
actionContexts.Add(new RaidKarazhanActionContext());
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidGruulsLairActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
actionContexts.Add(new RaidEoEActionContext());
|
||||
actionContexts.Add(new RaidVoAActionContext());
|
||||
@@ -149,6 +152,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
||||
triggerContexts.Add(new RaidKarazhanTriggerContext());
|
||||
triggerContexts.Add(new RaidMagtheridonTriggerContext());
|
||||
triggerContexts.Add(new RaidGruulsLairTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
triggerContexts.Add(new RaidVoATriggerContext());
|
||||
|
||||
@@ -1799,10 +1799,6 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
||||
{
|
||||
for (uint32 itemId : sRandomItemMgr.GetCachedEquipments(requiredLevel, inventoryType))
|
||||
{
|
||||
if (itemId == 46978) // shaman earth ring totem
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uint32 skipProb = 25;
|
||||
if (urand(1, 100) <= skipProb)
|
||||
continue;
|
||||
|
||||
@@ -185,6 +185,7 @@ PlayerbotAI::PlayerbotAI(Player* bot)
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_TRADE_STATUS_EXTENDED, "trade status extended");
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_RESPONSE, "loot response");
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_ITEM_PUSH_RESULT, "item push result");
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_LOOT_ROLL_WON, "loot roll won");
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_PARTY_COMMAND_RESULT, "party command");
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_LEVELUP_INFO, "levelup");
|
||||
botOutgoingPacketHandlers.AddHandler(SMSG_LOG_XPGAIN, "xpgain");
|
||||
@@ -1554,6 +1555,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 544:
|
||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||
break;
|
||||
case 548:
|
||||
strategyName = "ssc"; // Serpentshrine Cavern
|
||||
break;
|
||||
case 565:
|
||||
strategyName = "gruulslair"; // Gruul's Lair
|
||||
break;
|
||||
@@ -1585,7 +1589,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
strategyName = "wotlk-hol"; // Halls of Lightning
|
||||
break;
|
||||
case 603:
|
||||
strategyName = "uld"; // Ulduar
|
||||
strategyName = "ulduar"; // Ulduar
|
||||
break;
|
||||
case 604:
|
||||
strategyName = "wotlk-gd"; // Gundrak
|
||||
@@ -2580,7 +2584,7 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry)
|
||||
return name;
|
||||
}
|
||||
|
||||
std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
||||
std::vector<Player*> PlayerbotAI::GetRealPlayersInGroup()
|
||||
{
|
||||
std::vector<Player*> members;
|
||||
|
||||
@@ -2607,6 +2611,30 @@ std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
|
||||
return members;
|
||||
}
|
||||
|
||||
std::vector<Player*> PlayerbotAI::GetAllPlayersInGroup()
|
||||
{
|
||||
std::vector<Player*> members;
|
||||
|
||||
Group* group = bot->GetGroup();
|
||||
|
||||
if (!group)
|
||||
return members;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
|
||||
if (!member)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
members.push_back(ref->GetSource());
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
bool PlayerbotAI::SayToGuild(const std::string& msg)
|
||||
{
|
||||
if (msg.empty())
|
||||
@@ -2715,9 +2743,9 @@ bool PlayerbotAI::SayToParty(const std::string& msg)
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_PARTY, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(),
|
||||
bot->GetName());
|
||||
|
||||
for (auto reciever : GetPlayersInGroup())
|
||||
for (auto receiver : GetRealPlayersInGroup())
|
||||
{
|
||||
ServerFacade::instance().SendPacket(reciever, &data);
|
||||
ServerFacade::instance().SendPacket(receiver, &data);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -2732,9 +2760,9 @@ bool PlayerbotAI::SayToRaid(const std::string& msg)
|
||||
ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(),
|
||||
bot->GetName());
|
||||
|
||||
for (auto reciever : GetPlayersInGroup())
|
||||
for (auto receiver : GetRealPlayersInGroup())
|
||||
{
|
||||
ServerFacade::instance().SendPacket(reciever, &data);
|
||||
ServerFacade::instance().SendPacket(receiver, &data);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -446,7 +446,8 @@ public:
|
||||
GameObject* GetGameObject(ObjectGuid guid);
|
||||
// static GameObject* GetGameObject(GameObjectData const* gameObjectData);
|
||||
WorldObject* GetWorldObject(ObjectGuid guid);
|
||||
std::vector<Player*> GetPlayersInGroup();
|
||||
std::vector<Player*> GetAllPlayersInGroup();
|
||||
std::vector<Player*> GetRealPlayersInGroup();
|
||||
const AreaTableEntry* GetCurrentArea();
|
||||
const AreaTableEntry* GetCurrentZone();
|
||||
static std::string GetLocalizedAreaName(const AreaTableEntry* entry);
|
||||
|
||||
@@ -1784,7 +1784,7 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
|
||||
zone2LevelBracket[3525] = {10, 21}; // Bloodmyst Isle
|
||||
|
||||
// Classic WoW - High - level zones
|
||||
zone2LevelBracket[10] = {19, 33}; // Deadwind Pass
|
||||
zone2LevelBracket[10] = {19, 33}; // Duskwood
|
||||
zone2LevelBracket[11] = {21, 30}; // Wetlands
|
||||
zone2LevelBracket[44] = {16, 28}; // Redridge Mountains
|
||||
zone2LevelBracket[267] = {20, 34}; // Hillsbrad Foothills
|
||||
|
||||
@@ -2256,10 +2256,13 @@ void RandomItemMgr::BuildEquipCacheNew()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (itemId == 22784)
|
||||
{ // Sunwell Orb
|
||||
|
||||
// Unobtainable or unusable items
|
||||
if (itemId == 12468 || // Chilton Wand
|
||||
itemId == 22784 || // Sunwell Orb
|
||||
itemId == 46978) // Totem of the Earthen Ring
|
||||
continue;
|
||||
}
|
||||
|
||||
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -881,107 +881,6 @@ std::vector<GameObjectData const*> WorldPosition::getGameObjectsNear(float radiu
|
||||
return worker.GetResult();
|
||||
}
|
||||
|
||||
Creature* GuidPosition::GetCreature()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (loadedFromDB)
|
||||
{
|
||||
auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter());
|
||||
if (creatureBounds.first != creatureBounds.second)
|
||||
return creatureBounds.second->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getMap()->GetCreature(*this);
|
||||
}
|
||||
|
||||
Unit* GuidPosition::GetUnit()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (loadedFromDB)
|
||||
{
|
||||
auto creatureBounds = getMap()->GetCreatureBySpawnIdStore().equal_range(GetCounter());
|
||||
if (creatureBounds.first != creatureBounds.second)
|
||||
return creatureBounds.second->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsPlayer())
|
||||
return ObjectAccessor::FindPlayer(*this);
|
||||
|
||||
if (IsPet())
|
||||
return getMap()->GetPet(*this);
|
||||
|
||||
return GetCreature();
|
||||
}
|
||||
|
||||
GameObject* GuidPosition::GetGameObject()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (loadedFromDB)
|
||||
{
|
||||
auto gameobjectBounds = getMap()->GetGameObjectBySpawnIdStore().equal_range(GetCounter());
|
||||
if (gameobjectBounds.first != gameobjectBounds.second)
|
||||
return gameobjectBounds.second->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getMap()->GetGameObject(*this);
|
||||
}
|
||||
|
||||
Player* GuidPosition::GetPlayer()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (IsPlayer())
|
||||
return ObjectAccessor::FindPlayer(*this);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GuidPosition::isDead()
|
||||
{
|
||||
if (!getMap())
|
||||
return false;
|
||||
|
||||
if (!getMap()->IsGridLoaded(getX(), getY()))
|
||||
return false;
|
||||
|
||||
if (IsUnit() && GetUnit() && GetUnit()->IsInWorld() && GetUnit()->IsAlive())
|
||||
return false;
|
||||
|
||||
if (IsGameObject() && GetGameObject() && GetGameObject()->IsInWorld())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
GuidPosition::GuidPosition(WorldObject* wo) : ObjectGuid(wo->GetGUID()), WorldPosition(wo), loadedFromDB(false) {}
|
||||
|
||||
GuidPosition::GuidPosition(CreatureData const& creData)
|
||||
: ObjectGuid(HighGuid::Unit, creData.id1, creData.spawnId),
|
||||
WorldPosition(creData.mapid, creData.posX, creData.posY, creData.posZ, creData.orientation)
|
||||
{
|
||||
loadedFromDB = true;
|
||||
}
|
||||
|
||||
GuidPosition::GuidPosition(GameObjectData const& goData)
|
||||
: ObjectGuid(HighGuid::GameObject, goData.id),
|
||||
WorldPosition(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation)
|
||||
{
|
||||
loadedFromDB = true;
|
||||
}
|
||||
|
||||
CreatureTemplate const* GuidPosition::GetCreatureTemplate()
|
||||
{
|
||||
return IsCreature() ? sObjectMgr->GetCreatureTemplate(GetEntry()) : nullptr;
|
||||
@@ -1000,7 +899,7 @@ WorldObject* GuidPosition::GetWorldObject()
|
||||
switch (GetHigh())
|
||||
{
|
||||
case HighGuid::Player:
|
||||
return ObjectAccessor::FindPlayer(*this);
|
||||
return GetPlayer();
|
||||
case HighGuid::Transport:
|
||||
case HighGuid::Mo_Transport:
|
||||
case HighGuid::GameObject:
|
||||
@@ -1021,8 +920,93 @@ WorldObject* GuidPosition::GetWorldObject()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GameObject* GuidPosition::GetGameObject()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (loadedFromDB)
|
||||
return ObjectAccessor::GetSpawnedGameObjectByDBGUID(GetMapId(), GetCounter());
|
||||
|
||||
return getMap()->GetGameObject(*this); // fallback
|
||||
}
|
||||
|
||||
Unit* GuidPosition::GetUnit()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (IsPlayer())
|
||||
return GetPlayer();
|
||||
|
||||
if (IsPet())
|
||||
return getMap()->GetPet(*this);
|
||||
|
||||
return GetCreature();
|
||||
}
|
||||
|
||||
Creature* GuidPosition::GetCreature()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (loadedFromDB)
|
||||
return ObjectAccessor::GetSpawnedCreatureByDBGUID(GetMapId(), GetCounter());
|
||||
|
||||
return getMap()->GetCreature(*this); // fallback
|
||||
}
|
||||
|
||||
Player* GuidPosition::GetPlayer()
|
||||
{
|
||||
if (!*this)
|
||||
return nullptr;
|
||||
|
||||
if (IsPlayer())
|
||||
return ObjectAccessor::FindPlayer(*this);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GuidPosition::HasNpcFlag(NPCFlags flag) { return IsCreature() && GetCreatureTemplate()->npcflag & flag; }
|
||||
|
||||
bool GuidPosition::IsCreatureOrGOAccessible()
|
||||
{
|
||||
Map* map = getMap();
|
||||
if (!map || !map->IsGridLoaded(GetPositionX(), GetPositionY()))
|
||||
return false;
|
||||
|
||||
if (IsCreature())
|
||||
{
|
||||
Creature* creature = GetCreature();
|
||||
if (creature && creature->IsInWorld() && creature->IsAlive())
|
||||
return true;
|
||||
}
|
||||
else if (IsGameObject())
|
||||
{
|
||||
GameObject* go = GetGameObject();
|
||||
if (go && go->IsInWorld())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GuidPosition::GuidPosition(WorldObject* wo) : ObjectGuid(wo->GetGUID()), WorldPosition(wo), loadedFromDB(false) {}
|
||||
|
||||
GuidPosition::GuidPosition(CreatureData const& creData)
|
||||
: ObjectGuid(HighGuid::Unit, creData.id1, creData.spawnId),
|
||||
WorldPosition(creData.mapid, creData.posX, creData.posY, creData.posZ, creData.orientation)
|
||||
{
|
||||
loadedFromDB = true;
|
||||
}
|
||||
|
||||
GuidPosition::GuidPosition(GameObjectData const& goData)
|
||||
: ObjectGuid(HighGuid::GameObject, goData.id),
|
||||
WorldPosition(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation)
|
||||
{
|
||||
loadedFromDB = true;
|
||||
}
|
||||
|
||||
std::vector<WorldPosition*> TravelDestination::getPoints(bool ignoreFull)
|
||||
{
|
||||
if (ignoreFull)
|
||||
|
||||
@@ -416,14 +416,13 @@ public:
|
||||
GameObjectTemplate const* GetGameObjectTemplate();
|
||||
|
||||
WorldObject* GetWorldObject();
|
||||
Creature* GetCreature();
|
||||
Unit* GetUnit();
|
||||
GameObject* GetGameObject();
|
||||
Unit* GetUnit();
|
||||
Creature* GetCreature();
|
||||
Player* GetPlayer();
|
||||
|
||||
bool HasNpcFlag(NPCFlags flag);
|
||||
|
||||
bool isDead(); // For loaded grids check if the unit/object is unloaded/dead.
|
||||
bool IsCreatureOrGOAccessible(); // For loaded grids check if the creature/gameobject is in world + alive
|
||||
|
||||
operator bool() const { return !IsEmpty(); }
|
||||
bool operator==(ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); }
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
#include "PlayerbotOperation.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
#include "PlayerbotRepository.h"
|
||||
#include "RandomPlayerbotMgr.h"
|
||||
#include "UseMeetingStoneAction.h"
|
||||
#include "WorldSession.h"
|
||||
#include "WorldSessionMgr.h"
|
||||
|
||||
@@ -74,6 +76,15 @@ public:
|
||||
if (group->AddMember(target))
|
||||
{
|
||||
LOG_DEBUG("playerbots", "GroupInviteOperation: Successfully added {} to group", target->GetName());
|
||||
if (sPlayerbotAIConfig.summonWhenGroup && target->GetDistance(bot) > sPlayerbotAIConfig.sightDistance)
|
||||
{
|
||||
PlayerbotAI* targetAI = sPlayerbotsMgr.GetPlayerbotAI(target);
|
||||
if (targetAI)
|
||||
{
|
||||
SummonAction summonAction(targetAI, "group summon");
|
||||
summonAction.Teleport(bot, target, true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user