mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
Compare commits
21 Commits
99039199e0
...
b8adda0a90
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8adda0a90 | ||
|
|
cfcc29b86f | ||
|
|
9118c9671a | ||
|
|
8cb847db5d | ||
|
|
66d41e1d79 | ||
|
|
d2e5443109 | ||
|
|
6b0df4ff6c | ||
|
|
b8ff5996f8 | ||
|
|
826887133d | ||
|
|
7af675e712 | ||
|
|
8caf37af97 | ||
|
|
5d9761c9e8 | ||
|
|
38caa1daa7 | ||
|
|
ccce14238e | ||
|
|
104a1b9ee1 | ||
|
|
94195c3b9b | ||
|
|
063eabc16e | ||
|
|
cc6f6c2c3a | ||
|
|
c819516325 | ||
|
|
410ce134fe | ||
|
|
4a79a46da5 |
@ -32,7 +32,7 @@
|
||||
# LEVELS
|
||||
# GEAR
|
||||
# QUESTS
|
||||
# ACTIVITIES
|
||||
# ACTIVITY
|
||||
# SPELLS
|
||||
# STRATEGIES
|
||||
# RPG STRATEGY
|
||||
@ -2242,6 +2242,14 @@ AiPlayerbot.CommandPrefix = ""
|
||||
# Separator for bot chat commands
|
||||
AiPlayerbot.CommandSeparator = "\\\\"
|
||||
|
||||
# Enable automatic item count/trade trigger when a chat message contains
|
||||
# item-related keywords. When enabled (1), mentioning items in chat
|
||||
# (e.g. "food", "potion", "ammo") will automatically show inventory and
|
||||
# open a trade window with the bot. Explicit "c" and "t" commands still
|
||||
# work regardless of this setting.
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.EnableAutoTradeOnItemMention = 1
|
||||
|
||||
# Enable bots talking (say / yell / general chatting / lfg)
|
||||
AiPlayerbot.RandomBotTalk = 1
|
||||
# Enable bots emoting
|
||||
|
||||
@ -343,7 +343,7 @@ bool BGJoinAction::isUseful()
|
||||
return false;
|
||||
|
||||
// check Deserter debuff
|
||||
if (!bot->CanJoinToBattleground())
|
||||
if (bot->IsDeserter())
|
||||
return false;
|
||||
|
||||
// check if has free queue slots (pointless as already making sure not in queue)
|
||||
|
||||
@ -213,13 +213,7 @@ bool BuyAction::Execute(Event event)
|
||||
}
|
||||
}
|
||||
|
||||
if (!vendored)
|
||||
{
|
||||
botAI->TellError("There are no vendors nearby");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return vendored;
|
||||
}
|
||||
|
||||
bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, ItemTemplate const* proto)
|
||||
|
||||
@ -143,14 +143,17 @@ bool CastCustomSpellAction::Execute(Event event)
|
||||
std::ostringstream spellName;
|
||||
spellName << ChatHelper::FormatSpell(spellInfo) << " on ";
|
||||
|
||||
bool const hasItemTarget = itemTarget &&
|
||||
(spellInfo->Targets & TARGET_FLAG_ITEM || spellInfo->Targets & TARGET_FLAG_GAMEOBJECT_ITEM);
|
||||
|
||||
if (bot->GetTrader())
|
||||
spellName << "trade item";
|
||||
else if (itemTarget)
|
||||
else if (hasItemTarget)
|
||||
spellName << chat->FormatItem(itemTarget->GetTemplate());
|
||||
else if (target == bot)
|
||||
spellName << "self";
|
||||
else
|
||||
else if (target != bot)
|
||||
spellName << target->GetName();
|
||||
else
|
||||
spellName << "self";
|
||||
|
||||
if (!bot->GetTrader() && !botAI->CanCastSpell(spell, target, true, itemTarget))
|
||||
{
|
||||
|
||||
@ -9,11 +9,8 @@
|
||||
#include "PlayerbotRepository.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool ChangeCombatStrategyAction::Execute(Event event)
|
||||
{
|
||||
std::string const text = event.getParam();
|
||||
botAI->ChangeStrategy(text.empty() ? getName() : text, BOT_STATE_COMBAT);
|
||||
if (event.GetSource() == "co")
|
||||
// Helper function for prefixes used by combat and non-combat strategy commands.
|
||||
static void HandleStrategyCommon(PlayerbotAI* botAI, std::string const& text, BotState state)
|
||||
{
|
||||
std::vector<std::string> splitted = split(text, ',');
|
||||
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
|
||||
@ -26,12 +23,23 @@ bool ChangeCombatStrategyAction::Execute(Event event)
|
||||
case '~':
|
||||
PlayerbotRepository::instance().Save(botAI);
|
||||
break;
|
||||
case '!':
|
||||
botAI->SelectiveResetStrategies(state);
|
||||
PlayerbotRepository::instance().Save(botAI);
|
||||
break;
|
||||
case '?':
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ChangeCombatStrategyAction::Execute(Event event)
|
||||
{
|
||||
std::string const text = event.getParam();
|
||||
botAI->ChangeStrategy(text.empty() ? getName() : text, BOT_STATE_COMBAT);
|
||||
if (event.GetSource() == "co")
|
||||
HandleStrategyCommon(botAI, text, BOT_STATE_COMBAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -52,23 +60,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event)
|
||||
|
||||
botAI->ChangeStrategy(text, BOT_STATE_NON_COMBAT);
|
||||
if (event.GetSource() == "nc")
|
||||
{
|
||||
std::vector<std::string> splitted = split(text, ',');
|
||||
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
|
||||
{
|
||||
const char* name = i->c_str();
|
||||
switch (name[0])
|
||||
{
|
||||
case '+':
|
||||
case '-':
|
||||
case '~':
|
||||
PlayerbotRepository::instance().Save(botAI);
|
||||
break;
|
||||
case '?':
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
HandleStrategyCommon(botAI, text, BOT_STATE_NON_COMBAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
*/
|
||||
|
||||
#include "CheckMountStateAction.h"
|
||||
#include "AreaDefines.h"
|
||||
#include "BattleGroundTactics.h"
|
||||
#include "BattlegroundEY.h"
|
||||
#include "BattlegroundWS.h"
|
||||
#include "DBCStores.h"
|
||||
#include "Event.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
@ -14,6 +16,8 @@
|
||||
#include "ServerFacade.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
|
||||
static constexpr uint32 SPELL_COLD_WEATHER_FLYING = 54197;
|
||||
|
||||
// Define the static map / init bool for caching bot preferred mount data globally
|
||||
std::unordered_map<uint32, PreferredMountCache> CheckMountStateAction::mountCache;
|
||||
bool CheckMountStateAction::preferredMountTableChecked = false;
|
||||
@ -94,9 +98,10 @@ bool CheckMountStateAction::Execute(Event /*event*/)
|
||||
}
|
||||
|
||||
bool inBattleground = bot->InBattleground();
|
||||
bool const noRealMaster = (!master || master == bot);
|
||||
|
||||
// If there is a master and bot not in BG, follow master's mount state regardless of group leader
|
||||
if (master && !inBattleground)
|
||||
if (!noRealMaster && !inBattleground)
|
||||
{
|
||||
if (ShouldFollowMasterMountState(master, noAttackers, shouldMount))
|
||||
return Mount();
|
||||
@ -110,8 +115,8 @@ bool CheckMountStateAction::Execute(Event /*event*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no master or bot in BG
|
||||
if ((!master || inBattleground) && !bot->IsMounted() &&
|
||||
// No real master (random bot or self-bot) OR bot in BG
|
||||
if ((noRealMaster || inBattleground) && !bot->IsMounted() &&
|
||||
noAttackers && shouldMount && !bot->IsInCombat())
|
||||
return Mount();
|
||||
|
||||
@ -228,6 +233,39 @@ void CheckMountStateAction::Dismount()
|
||||
|
||||
WorldPacket emptyPacket;
|
||||
bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
|
||||
|
||||
bool const wantsFly = bot->HasIncreaseMountedFlightSpeedAura() || bot->HasFlyAura();
|
||||
bool const isWaterWalking = bot->HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING);
|
||||
bool const isFlying = bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
|
||||
bool const hasGravityDisabled = bot->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY);
|
||||
if (!wantsFly && !isWaterWalking && (isFlying || hasGravityDisabled))
|
||||
{
|
||||
bot->RemoveUnitMovementFlag(
|
||||
MOVEMENTFLAG_FLYING | MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY);
|
||||
if (!bot->IsRooted())
|
||||
bot->SendMovementFlagUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void CheckMountStateAction::CompleteDismount(Player* bot)
|
||||
{
|
||||
if (!bot || !bot->IsInWorld())
|
||||
return;
|
||||
|
||||
float const x = bot->GetPositionX();
|
||||
float const y = bot->GetPositionY();
|
||||
float const startZ = bot->GetPositionZ();
|
||||
|
||||
float groundZ = startZ;
|
||||
bot->UpdateAllowedPositionZ(x, y, groundZ);
|
||||
|
||||
bot->GetMotionMaster()->MoveFall();
|
||||
MovementInfo fallInfo = bot->m_movementInfo;
|
||||
// Need to set the start of the fall, otherwise the fall may start from too high of a Z and kill the bot.
|
||||
bot->SetFallInformation(0, startZ);
|
||||
fallInfo.pos.Relocate(x, y, groundZ);
|
||||
bot->HandleFall(fallInfo);
|
||||
bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR);
|
||||
}
|
||||
|
||||
bool CheckMountStateAction::TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const
|
||||
@ -434,6 +472,24 @@ bool CheckMountStateAction::ShouldDismountForMaster(Player* master) const
|
||||
return !isMasterMounted && bot->IsMounted();
|
||||
}
|
||||
|
||||
static bool BotCanUseFlyingMount(Player const* bot)
|
||||
{
|
||||
if (bot->GetPureSkillValue(SKILL_RIDING) < 225)
|
||||
return false;
|
||||
|
||||
AreaTableEntry const* area = sAreaTableStore.LookupEntry(bot->GetAreaId());
|
||||
if (!area || !area->IsFlyable())
|
||||
return false;
|
||||
if (area->flags & AREA_FLAG_NO_FLY_ZONE)
|
||||
return false;
|
||||
|
||||
uint32 const vmap = GetVirtualMapForMapAndZone(bot->GetMapId(), bot->GetZoneId());
|
||||
if (vmap == MAP_NORTHREND && !bot->HasSpell(SPELL_COLD_WEATHER_FLYING))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const MountData& mountData) const
|
||||
{
|
||||
// Check riding skill and level requirements
|
||||
@ -443,8 +499,10 @@ int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const Mou
|
||||
if (ridingSkill <= 75 && botLevel < static_cast<int32>(sPlayerbotAIConfig.useFastGroundMountAtMinLevel))
|
||||
return 59;
|
||||
|
||||
// If there is a master and bot not in BG, use master's aura effects.
|
||||
if (master && !bot->InBattleground())
|
||||
// check if bot has master and if master is self
|
||||
bool const noRealMaster = (!master || master == bot);
|
||||
|
||||
if (!noRealMaster && !bot->InBattleground())
|
||||
{
|
||||
auto auraEffects = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED);
|
||||
if (!auraEffects.empty())
|
||||
@ -458,27 +516,27 @@ int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const Mou
|
||||
return 279;
|
||||
else if (masterInShapeshiftForm == FORM_FLIGHT)
|
||||
return 149;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bots on their own.
|
||||
int32 speed = mountData.maxSpeed;
|
||||
if (bot->InBattleground() && speed > 99)
|
||||
return 99;
|
||||
|
||||
return speed;
|
||||
return 59; // walk pace
|
||||
}
|
||||
|
||||
return 59;
|
||||
// No real master OR battleground: pick speed by skill tier.
|
||||
if (!bot->InBattleground() && BotCanUseFlyingMount(bot))
|
||||
return (ridingSkill >= 300) ? 279 : 149;
|
||||
|
||||
int32 maxGround = (ridingSkill >= 150) ? 99 : 59;
|
||||
if (bot->InBattleground() && maxGround > 99)
|
||||
maxGround = 99;
|
||||
return maxGround;
|
||||
}
|
||||
|
||||
uint32 CheckMountStateAction::GetMountType(Player* master) const
|
||||
{
|
||||
if (!master)
|
||||
return 0;
|
||||
bool const noRealMaster = (!master || master == bot);
|
||||
|
||||
if (noRealMaster)
|
||||
return (!bot->InBattleground() && BotCanUseFlyingMount(bot)) ? 1 : 0;
|
||||
|
||||
auto auraEffects = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED);
|
||||
|
||||
if (!auraEffects.empty())
|
||||
{
|
||||
SpellInfo const* masterSpell = auraEffects.front()->GetSpellInfo();
|
||||
|
||||
@ -42,6 +42,8 @@ public:
|
||||
bool isPossible() override { return true; }
|
||||
bool Mount();
|
||||
|
||||
static void CompleteDismount(Player* bot);
|
||||
|
||||
private:
|
||||
Player* master;
|
||||
ShapeshiftForm masterInShapeshiftForm;
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
#include "WorldPacket.h"
|
||||
#include "Group.h"
|
||||
#include "Chat.h"
|
||||
#include "Ai/Base/Util/GenericBuffUtils.h"
|
||||
#include "GenericBuffUtils.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
using ai::buff::MakeAuraQualifierForBuff;
|
||||
@ -134,7 +134,8 @@ bool CastSpellAction::isPossible()
|
||||
return botAI->CanCastSpell(spell, GetTarget());
|
||||
}
|
||||
|
||||
CastMeleeSpellAction::CastMeleeSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
|
||||
CastMeleeSpellAction::CastMeleeSpellAction(
|
||||
PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
|
||||
{
|
||||
range = ATTACK_DISTANCE;
|
||||
}
|
||||
@ -182,56 +183,47 @@ bool CastAuraSpellAction::isUseful()
|
||||
return false;
|
||||
}
|
||||
|
||||
CastEnchantItemAction::CastEnchantItemAction(PlayerbotAI* botAI, std::string const spell)
|
||||
: CastSpellAction(botAI, spell)
|
||||
CastEnchantItemMainHandAction::CastEnchantItemMainHandAction(
|
||||
PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
||||
|
||||
bool CastEnchantItemMainHandAction::Execute(Event /*event*/)
|
||||
{
|
||||
range = botAI->GetRange("spell");
|
||||
Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||
return item && botAI->CastSpell(spell, bot, item);
|
||||
}
|
||||
|
||||
bool CastEnchantItemAction::isPossible()
|
||||
{
|
||||
// if (!CastSpellAction::isPossible())
|
||||
// {
|
||||
// botAI->TellMasterNoFacing("Impossible: " + spell);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
uint32 spellId = AI_VALUE2(uint32, "spell id", spell);
|
||||
|
||||
// bool ok = AI_VALUE2(Item*, "item for spell", spellId);
|
||||
// Item* item = AI_VALUE2(Item*, "item for spell", spellId);
|
||||
// botAI->TellMasterNoFacing("spell: " + spell + ", spell id: " + std::to_string(spellId) + " item for spell: " +
|
||||
// std::to_string(ok));
|
||||
return spellId && AI_VALUE2(Item*, "item for spell", spellId);
|
||||
}
|
||||
|
||||
CastEnchantItemMainHandAction::CastEnchantItemMainHandAction(PlayerbotAI* botAI, std::string const spell)
|
||||
: CastEnchantItemAction(botAI, spell) {}
|
||||
|
||||
bool CastEnchantItemMainHandAction::isPossible()
|
||||
{
|
||||
if (!CastEnchantItemAction::isPossible())
|
||||
return false;
|
||||
|
||||
Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
|
||||
return item && !item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) &&
|
||||
item->GetTemplate()->Class == ITEM_CLASS_WEAPON;
|
||||
if (!item || item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_MISC ||
|
||||
item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE ||
|
||||
item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CastEnchantItemOffHandAction::CastEnchantItemOffHandAction(PlayerbotAI* botAI, std::string const spell)
|
||||
: CastEnchantItemAction(botAI, spell) {}
|
||||
return botAI->CanCastSpell(spell, bot, item);
|
||||
}
|
||||
|
||||
CastEnchantItemOffHandAction::CastEnchantItemOffHandAction(
|
||||
PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell) {}
|
||||
|
||||
bool CastEnchantItemOffHandAction::Execute(Event /*event*/)
|
||||
{
|
||||
Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
|
||||
return item && botAI->CastSpell(spell, bot, item);
|
||||
}
|
||||
|
||||
bool CastEnchantItemOffHandAction::isPossible()
|
||||
{
|
||||
if (!CastEnchantItemAction::isPossible())
|
||||
return false;
|
||||
|
||||
Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
|
||||
if (!item || item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
if (!item || item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_MISC ||
|
||||
item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 invType = item->GetTemplate()->InventoryType;
|
||||
return invType == INVTYPE_WEAPON || invType == INVTYPE_WEAPONOFFHAND;
|
||||
return botAI->CanCastSpell(spell, bot, item);
|
||||
}
|
||||
|
||||
CastHealingSpellAction::CastHealingSpellAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount,
|
||||
@ -245,7 +237,8 @@ bool CastHealingSpellAction::isUseful() { return CastAuraSpellAction::isUseful()
|
||||
|
||||
bool CastAoeHealSpellAction::isUseful() { return CastSpellAction::isUseful(); }
|
||||
|
||||
CastCureSpellAction::CastCureSpellAction(PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
|
||||
CastCureSpellAction::CastCureSpellAction(
|
||||
PlayerbotAI* botAI, std::string const spell) : CastSpellAction(botAI, spell)
|
||||
{
|
||||
range = botAI->GetRange("heal");
|
||||
}
|
||||
@ -267,13 +260,15 @@ bool BuffOnPartyAction::Execute(Event /*event*/)
|
||||
std::string castName = spell; // default = mono
|
||||
|
||||
auto SendGroupRP = ai::chat::MakeGroupAnnouncer(bot);
|
||||
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, SendGroupRP);
|
||||
castName = ai::buff::UpgradeToGroupIfAppropriate(
|
||||
bot, botAI, castName, /*announceOnMissing=*/true, SendGroupRP);
|
||||
|
||||
return botAI->CastSpell(castName, GetTarget());
|
||||
}
|
||||
// End greater buff fix
|
||||
|
||||
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0)
|
||||
CastShootAction::CastShootAction(
|
||||
PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot"), shootSpellId(0)
|
||||
{
|
||||
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||
{
|
||||
@ -327,7 +322,8 @@ Value<Unit*>* CastDebuffSpellOnMeleeAttackerAction::GetTargetValue()
|
||||
return context->GetValue<Unit*>("melee attacker without aura", spell);
|
||||
}
|
||||
|
||||
CastBuffSpellAction::CastBuffSpellAction(PlayerbotAI* botAI, std::string const spell, bool checkIsOwner, uint32 beforeDuration)
|
||||
CastBuffSpellAction::CastBuffSpellAction(
|
||||
PlayerbotAI* botAI, std::string const spell, bool checkIsOwner, uint32 beforeDuration)
|
||||
: CastAuraSpellAction(botAI, spell, checkIsOwner, false, beforeDuration)
|
||||
{
|
||||
range = botAI->GetRange("spell");
|
||||
@ -448,7 +444,8 @@ bool UseTrinketAction::UseTrinket(Item* item)
|
||||
uint32 spellId = 0;
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
{
|
||||
if (item->GetTemplate()->Spells[i].SpellId > 0 && item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
|
||||
if (item->GetTemplate()->Spells[i].SpellId > 0 &&
|
||||
item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
|
||||
{
|
||||
spellId = item->GetTemplate()->Spells[i].SpellId;
|
||||
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
|
||||
@ -121,26 +121,21 @@ public:
|
||||
std::string const GetTargetName() override { return "self target"; }
|
||||
};
|
||||
|
||||
class CastEnchantItemAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastEnchantItemAction(PlayerbotAI* botAI, std::string const spell);
|
||||
|
||||
bool isPossible() override;
|
||||
std::string const GetTargetName() override { return "self target"; }
|
||||
};
|
||||
|
||||
class CastEnchantItemMainHandAction : public CastEnchantItemAction
|
||||
class CastEnchantItemMainHandAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastEnchantItemMainHandAction(PlayerbotAI* botAI, std::string const spell);
|
||||
std::string const GetTargetName() override { return "self target"; }
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
};
|
||||
|
||||
class CastEnchantItemOffHandAction : public CastEnchantItemAction
|
||||
class CastEnchantItemOffHandAction : public CastSpellAction
|
||||
{
|
||||
public:
|
||||
CastEnchantItemOffHandAction(PlayerbotAI* botAI, std::string const spell);
|
||||
std::string const GetTargetName() override { return "self target"; }
|
||||
bool Execute(Event event) override;
|
||||
bool isPossible() override;
|
||||
};
|
||||
|
||||
|
||||
@ -296,7 +296,7 @@ bool PetitionTurnInAction::isUseful()
|
||||
|
||||
bool BuyTabardAction::Execute(Event /*event*/)
|
||||
{
|
||||
bool canBuy = botAI->DoSpecificAction("buy", Event("buy tabard", "Hitem:5976:"));
|
||||
bool canBuy = botAI->DoSpecificAction("buy", Event("buy tabard", "Hitem:5976:"), true);
|
||||
if (canBuy && AI_VALUE2(uint32, "item count", chat->FormatQItem(5976)))
|
||||
return true;
|
||||
|
||||
|
||||
@ -10,6 +10,31 @@
|
||||
#include "ItemVisitors.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isReservedQualifier(std::string const& text)
|
||||
{
|
||||
static std::array<std::string_view, 13> const exactQualifiers = {
|
||||
"ammo",
|
||||
"conjured drink",
|
||||
"conjured food",
|
||||
"conjured water",
|
||||
"drink",
|
||||
"food",
|
||||
"healing potion",
|
||||
"mount",
|
||||
"mana potion",
|
||||
"pet",
|
||||
"quest",
|
||||
"recipe",
|
||||
"water"
|
||||
};
|
||||
|
||||
return std::find(exactQualifiers.begin(), exactQualifiers.end(), text) != exactQualifiers.end() ||
|
||||
text.rfind("usage ", 0) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryAction::IterateItems(IterateItemsVisitor* visitor, IterateItemsMask mask)
|
||||
{
|
||||
if (mask & ITERATE_ITEMS_IN_BAGS)
|
||||
@ -292,9 +317,12 @@ std::vector<Item*> InventoryAction::parseItems(std::string const text, IterateIt
|
||||
found.insert(visitor.GetResult().begin(), visitor.GetResult().end());
|
||||
}
|
||||
|
||||
if (!isReservedQualifier(text))
|
||||
{
|
||||
FindNamedItemVisitor visitor(bot, text);
|
||||
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
|
||||
found.insert(visitor.GetResult().begin(), visitor.GetResult().end());
|
||||
}
|
||||
|
||||
uint32 quality = chat->parseItemQuality(text);
|
||||
if (quality != MAX_ITEM_QUALITY)
|
||||
|
||||
39
src/Ai/Base/Actions/TellEmblemsAction.cpp
Normal file
39
src/Ai/Base/Actions/TellEmblemsAction.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "TellEmblemsAction.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Event.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
bool TellEmblemsAction::Execute(Event /*event*/)
|
||||
{
|
||||
static std::array<uint32, 6> const emblemIds = {
|
||||
29434, // Badge of Justice
|
||||
40752, // Emblem of Heroism
|
||||
40753, // Emblem of Valor
|
||||
45624, // Emblem of Conquest
|
||||
47241, // Emblem of Triumph
|
||||
49426 // Emblem of Frost
|
||||
};
|
||||
|
||||
botAI->TellMaster("=== Emblems ===");
|
||||
|
||||
for (uint32 itemId : emblemIds)
|
||||
{
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
|
||||
uint32 count = bot->GetItemCount(itemId, false);
|
||||
std::ostringstream out;
|
||||
out << chat->FormatItem(proto, count);
|
||||
botAI->TellMaster(out);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
21
src/Ai/Base/Actions/TellEmblemsAction.h
Normal file
21
src/Ai/Base/Actions/TellEmblemsAction.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_TELLEMBLEMSACTION_H
|
||||
#define _PLAYERBOT_TELLEMBLEMSACTION_H
|
||||
|
||||
#include "InventoryAction.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
|
||||
class TellEmblemsAction : public InventoryAction
|
||||
{
|
||||
public:
|
||||
TellEmblemsAction(PlayerbotAI* botAI) : InventoryAction(botAI, "emblems") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -5,34 +5,23 @@
|
||||
|
||||
#include "TellReputationAction.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Event.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "ReputationMgr.h"
|
||||
|
||||
bool TellReputationAction::Execute(Event /*event*/)
|
||||
#include "SharedDefines.h"
|
||||
|
||||
std::string TellReputationAction::BuildReputationLine(FactionEntry const* entry)
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return false;
|
||||
|
||||
ObjectGuid selection = master->GetTarget();
|
||||
if (selection.IsEmpty())
|
||||
return false;
|
||||
|
||||
Unit* unit = ObjectAccessor::GetUnit(*master, selection);
|
||||
if (!unit)
|
||||
return false;
|
||||
|
||||
FactionTemplateEntry const* factionTemplate = unit->GetFactionTemplateEntry();
|
||||
uint32 faction = factionTemplate->faction;
|
||||
FactionEntry const* entry = sFactionStore.LookupEntry(faction);
|
||||
int32 reputation = bot->GetReputationMgr().GetReputation(faction);
|
||||
ReputationMgr& repMgr = bot->GetReputationMgr();
|
||||
ReputationRank rank = repMgr.GetRank(entry);
|
||||
int32 reputation = repMgr.GetReputation(entry->ID);
|
||||
|
||||
std::ostringstream out;
|
||||
out << entry->name[0] << ": ";
|
||||
out << "|cff";
|
||||
out << entry->name[0] << ": |cff";
|
||||
|
||||
ReputationRank rank = bot->GetReputationMgr().GetRank(entry);
|
||||
switch (rank)
|
||||
{
|
||||
case REP_HATED:
|
||||
@ -71,7 +60,65 @@ bool TellReputationAction::Execute(Event /*event*/)
|
||||
base -= ReputationMgr::PointsInRank[i];
|
||||
|
||||
out << " (" << (reputation - base) << "/" << ReputationMgr::PointsInRank[rank] << ")";
|
||||
botAI->TellMaster(out);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
bool TellReputationAction::Execute(Event event)
|
||||
{
|
||||
std::string const param = event.getParam();
|
||||
if (param == "all")
|
||||
{
|
||||
ReputationMgr& repMgr = bot->GetReputationMgr();
|
||||
std::vector<std::string> lines;
|
||||
|
||||
FactionStateList const& stateList = repMgr.GetStateList();
|
||||
lines.reserve(stateList.size());
|
||||
|
||||
for (auto const& itr : stateList)
|
||||
{
|
||||
FactionState const& faction = itr.second;
|
||||
if (!(faction.Flags & FACTION_FLAG_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (faction.Flags & (FACTION_FLAG_HIDDEN | FACTION_FLAG_INVISIBLE_FORCED) &&
|
||||
!(faction.Flags & FACTION_FLAG_SPECIAL))
|
||||
continue;
|
||||
|
||||
FactionEntry const* entry = sFactionStore.LookupEntry(faction.ID);
|
||||
if (!entry)
|
||||
continue;
|
||||
|
||||
lines.push_back(BuildReputationLine(entry));
|
||||
}
|
||||
|
||||
std::sort(lines.begin(), lines.end());
|
||||
|
||||
botAI->TellMaster("=== Reputations ===");
|
||||
for (auto const& line : lines)
|
||||
botAI->TellMaster(line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Player* master = GetMaster();
|
||||
if (!master)
|
||||
return false;
|
||||
|
||||
ObjectGuid selection = master->GetTarget();
|
||||
if (selection.IsEmpty())
|
||||
return false;
|
||||
|
||||
Unit* unit = ObjectAccessor::GetUnit(*master, selection);
|
||||
if (!unit)
|
||||
return false;
|
||||
|
||||
FactionTemplateEntry const* factionTemplate = unit->GetFactionTemplateEntry();
|
||||
|
||||
FactionEntry const* entry = sFactionStore.LookupEntry(factionTemplate->faction);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
botAI->TellMaster(BuildReputationLine(entry));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6,8 +6,11 @@
|
||||
#ifndef _PLAYERBOT_TELLREPUTATIONACTION_H
|
||||
#define _PLAYERBOT_TELLREPUTATIONACTION_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Action.h"
|
||||
|
||||
struct FactionEntry;
|
||||
class PlayerbotAI;
|
||||
|
||||
class TellReputationAction : public Action
|
||||
@ -16,6 +19,9 @@ public:
|
||||
TellReputationAction(PlayerbotAI* botAI) : Action(botAI, "reputation") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
std::string BuildReputationLine(FactionEntry const* entry);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -66,6 +66,7 @@
|
||||
#include "TaxiAction.h"
|
||||
#include "TeleportAction.h"
|
||||
#include "TellCastFailedAction.h"
|
||||
#include "TellEmblemsAction.h"
|
||||
#include "TellItemCountAction.h"
|
||||
#include "TellLosAction.h"
|
||||
#include "TellReputationAction.h"
|
||||
@ -120,6 +121,7 @@ public:
|
||||
creators["teleport"] = &ChatActionContext::teleport;
|
||||
creators["taxi"] = &ChatActionContext::taxi;
|
||||
creators["repair"] = &ChatActionContext::repair;
|
||||
creators["emblems"] = &ChatActionContext::emblems;
|
||||
creators["use"] = &ChatActionContext::use;
|
||||
creators["item count"] = &ChatActionContext::item_count;
|
||||
creators["equip"] = &ChatActionContext::equip;
|
||||
@ -276,6 +278,7 @@ private:
|
||||
static Action* item_count(PlayerbotAI* botAI) { return new TellItemCountAction(botAI); }
|
||||
static Action* use(PlayerbotAI* botAI) { return new UseItemAction(botAI); }
|
||||
static Action* repair(PlayerbotAI* botAI) { return new RepairAllAction(botAI); }
|
||||
static Action* emblems(PlayerbotAI* botAI) { return new TellEmblemsAction(botAI); }
|
||||
static Action* taxi(PlayerbotAI* botAI) { return new TaxiAction(botAI); }
|
||||
static Action* teleport(PlayerbotAI* botAI) { return new TeleportAction(botAI); }
|
||||
static Action* release(PlayerbotAI* botAI) { return new ReleaseSpiritAction(botAI); }
|
||||
|
||||
@ -41,6 +41,7 @@ public:
|
||||
creators["teleport"] = &ChatTriggerContext::teleport;
|
||||
creators["taxi"] = &ChatTriggerContext::taxi;
|
||||
creators["repair"] = &ChatTriggerContext::repair;
|
||||
creators["emblems"] = &ChatTriggerContext::emblems;
|
||||
creators["u"] = &ChatTriggerContext::use;
|
||||
creators["use"] = &ChatTriggerContext::use;
|
||||
creators["c"] = &ChatTriggerContext::item_count;
|
||||
@ -235,6 +236,7 @@ private:
|
||||
static Trigger* item_count(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "c"); }
|
||||
static Trigger* use(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "use"); }
|
||||
static Trigger* repair(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "repair"); }
|
||||
static Trigger* emblems(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "emblems"); }
|
||||
static Trigger* taxi(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "taxi"); }
|
||||
static Trigger* teleport(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "teleport"); }
|
||||
static Trigger* q(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "q"); }
|
||||
|
||||
@ -114,6 +114,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
triggers.push_back(new TriggerNode("pet attack", { NextAction("pet attack", relevance) }));
|
||||
triggers.push_back(new TriggerNode("roll", { NextAction("roll", relevance) }));
|
||||
triggers.push_back(new TriggerNode("focus heal", { NextAction("focus heal targets", relevance) }));
|
||||
triggers.push_back(new TriggerNode("emblems", { NextAction("emblems", relevance) }));
|
||||
}
|
||||
|
||||
ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||
@ -138,6 +139,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
||||
supported.push_back("teleport");
|
||||
supported.push_back("taxi");
|
||||
supported.push_back("repair");
|
||||
supported.push_back("emblems");
|
||||
supported.push_back("talents");
|
||||
supported.push_back("spells");
|
||||
supported.push_back("co");
|
||||
@ -202,8 +204,8 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas
|
||||
supported.push_back("unlock items");
|
||||
supported.push_back("unlock traded item");
|
||||
supported.push_back("tame");
|
||||
supported.push_back("glyphs"); // Added for custom Glyphs
|
||||
supported.push_back("glyph equip"); // Added for custom Glyphs
|
||||
supported.push_back("glyphs");
|
||||
supported.push_back("glyph equip");
|
||||
supported.push_back("pet");
|
||||
supported.push_back("pet attack");
|
||||
supported.push_back("wait for attack time");
|
||||
|
||||
@ -39,11 +39,8 @@ bool LeaveLargeGuildTrigger::IsActive()
|
||||
|
||||
Player* leader = ObjectAccessor::FindPlayer(guild->GetLeaderGUID());
|
||||
|
||||
// Only leave the guild if we know the leader is not a real player.
|
||||
if (!leader || !GET_PLAYERBOT_AI(leader) || !GET_PLAYERBOT_AI(leader)->IsRealPlayer())
|
||||
return false;
|
||||
|
||||
PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(leader);
|
||||
// Only leave the guild if the leader is an online bot (not a real player).
|
||||
PlayerbotAI* leaderBotAI = leader ? GET_PLAYERBOT_AI(leader) : nullptr;
|
||||
if (!leaderBotAI || leaderBotAI->IsRealPlayer())
|
||||
return false;
|
||||
|
||||
|
||||
@ -297,7 +297,7 @@ bool PlayerWantsInBattlegroundTrigger::IsActive()
|
||||
if (bot->GetBattleground() && bot->GetBattleground()->GetStatus() == STATUS_IN_PROGRESS)
|
||||
return false;
|
||||
|
||||
if (!bot->CanJoinToBattleground())
|
||||
if (bot->IsDeserter())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@ -180,19 +180,11 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
delete pItem;
|
||||
|
||||
if (result != EQUIP_ERR_OK && result != EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
// Check is unique items are equipped or not
|
||||
bool needToCheckUnique = false;
|
||||
if (result == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
|
||||
{
|
||||
needToCheckUnique = true;
|
||||
}
|
||||
else if (itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE))
|
||||
{
|
||||
needToCheckUnique = true;
|
||||
}
|
||||
|
||||
// Check if unique items are equipped or not
|
||||
bool needToCheckUnique = result == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS ||
|
||||
itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE);
|
||||
|
||||
if (needToCheckUnique)
|
||||
{
|
||||
@ -206,14 +198,11 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
bool isEquipped = (totalItemCount > bagItemCount);
|
||||
|
||||
if (isEquipped)
|
||||
{
|
||||
return ITEM_USAGE_NONE; // Item is already equipped
|
||||
}
|
||||
// If not equipped, continue processing
|
||||
}
|
||||
|
||||
if (itemProto->Class == ITEM_CLASS_QUIVER)
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
if (itemProto->Class == ITEM_CLASS_QUIVER && bot->getClass() != CLASS_HUNTER)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
if (itemProto->Class == ITEM_CLASS_CONTAINER)
|
||||
@ -221,13 +210,15 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
if (itemProto->SubClass != ITEM_SUBCLASS_CONTAINER)
|
||||
return ITEM_USAGE_NONE; // Todo add logic for non-bag containers. We want to look at professions/class and
|
||||
// only replace if non-bag is larger than bag.
|
||||
|
||||
if (GetSmallestBagSize() >= itemProto->ContainerSlots)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
|
||||
if (itemProto->Class == ITEM_CLASS_WEAPON && itemProto->SubClass == ITEM_SUBCLASS_WEAPON_MISC)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
bool shouldEquip = false;
|
||||
// uint32 statWeight = sRandomItemMgr.GetLiveStatWeight(bot, itemProto->ItemId);
|
||||
StatsWeightCalculator calculator(bot);
|
||||
@ -254,19 +245,14 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true);
|
||||
// Check if dest wasn't set correctly by CanEquipItem and use FindEquipSlot instead
|
||||
// This occurs with unique items that are already in the bots bags when CanEquipItem is called
|
||||
if (dest == 0)
|
||||
{
|
||||
if (dstSlot != NULL_SLOT)
|
||||
if (dest == 0 && dstSlot != NULL_SLOT)
|
||||
{
|
||||
// Construct dest from dstSlot
|
||||
dest = (INVENTORY_SLOT_BAG_0 << 8) | dstSlot;
|
||||
}
|
||||
}
|
||||
|
||||
if (dstSlot == EQUIPMENT_SLOT_FINGER1 || dstSlot == EQUIPMENT_SLOT_TRINKET1)
|
||||
{
|
||||
possibleSlots = 2;
|
||||
}
|
||||
|
||||
// Check weapon case separately to keep things a bit cleaner
|
||||
bool have2HWeapon = false;
|
||||
@ -283,14 +269,9 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
itemProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2);
|
||||
|
||||
// If the bot can Titan Grip, ignore any 2H weapon that isn't a 2H sword, mace, or axe.
|
||||
if (bot->CanTitanGrip())
|
||||
{
|
||||
// If this weapon is 2H but not one of the valid TG weapon types, do not equip it at all.
|
||||
if (itemProto->InventoryType == INVTYPE_2HWEAPON && !isValidTGWeapon)
|
||||
{
|
||||
if (bot->CanTitanGrip() && itemProto->InventoryType == INVTYPE_2HWEAPON && !isValidTGWeapon)
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Now handle the logic for equipping and possible offhand slots
|
||||
// If the bot can Dual Wield and:
|
||||
@ -317,10 +298,8 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
if (shouldEquipInSlot)
|
||||
return ITEM_USAGE_EQUIP;
|
||||
else
|
||||
{
|
||||
return ITEM_USAGE_BAD_EQUIP;
|
||||
}
|
||||
}
|
||||
|
||||
ItemTemplate const* oldItemProto = oldItem->GetTemplate();
|
||||
float oldScore = calculator.CalculateItem(oldItemProto->ItemId, oldItem->GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID));
|
||||
@ -328,23 +307,17 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto,
|
||||
{
|
||||
// uint32 oldStatWeight = sRandomItemMgr.GetLiveStatWeight(bot, oldItemProto->ItemId);
|
||||
if (itemScore || oldScore)
|
||||
{
|
||||
shouldEquipInSlot = itemScore > oldScore * sPlayerbotAIConfig.equipUpgradeThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
// Bigger quiver
|
||||
if (itemProto->Class == ITEM_CLASS_QUIVER)
|
||||
{
|
||||
if (!oldItem || oldItemProto->ContainerSlots < itemProto->ContainerSlots)
|
||||
{
|
||||
return ITEM_USAGE_EQUIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ITEM_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool existingShouldEquip = true;
|
||||
if (oldItemProto->Class == ITEM_CLASS_WEAPON && !sRandomItemMgr.CanEquipWeapon(bot->getClass(), oldItemProto))
|
||||
|
||||
@ -11,6 +11,22 @@
|
||||
#include "AoeValues.h"
|
||||
#include "TargetValue.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PrepareThornsTarget(PlayerbotAI* botAI, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
Aura* existingThorns = botAI->GetAura("thorns", target, true);
|
||||
if (!existingThorns)
|
||||
return true;
|
||||
|
||||
target->RemoveOwnedAura(existingThorns, AURA_REMOVE_BY_CANCEL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<NextAction> CastAbolishPoisonAction::getAlternatives()
|
||||
{
|
||||
return NextAction::merge({ NextAction("cure poison") },
|
||||
@ -33,6 +49,21 @@ bool CastLifebloomOnMainTankAction::isUseful()
|
||||
return !lifebloom || lifebloom->GetStackAmount() < 3 || lifebloom->GetDuration() < 2000;
|
||||
}
|
||||
|
||||
bool CastThornsAction::Execute(Event event)
|
||||
{
|
||||
return PrepareThornsTarget(botAI, GetTarget()) && CastBuffSpellAction::Execute(event);
|
||||
}
|
||||
|
||||
bool CastThornsOnPartyAction::Execute(Event event)
|
||||
{
|
||||
return PrepareThornsTarget(botAI, GetTarget()) && BuffOnPartyAction::Execute(event);
|
||||
}
|
||||
|
||||
bool CastThornsOnMainTankAction::Execute(Event event)
|
||||
{
|
||||
return PrepareThornsTarget(botAI, GetTarget()) && BuffOnMainTankAction::Execute(event);
|
||||
}
|
||||
|
||||
Value<Unit*>* CastEntanglingRootsCcAction::GetTargetValue()
|
||||
{
|
||||
return context->GetValue<Unit*>("cc target", "entangling roots");
|
||||
|
||||
@ -114,18 +114,24 @@ class CastThornsAction : public CastBuffSpellAction
|
||||
{
|
||||
public:
|
||||
CastThornsAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "thorns") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class CastThornsOnPartyAction : public BuffOnPartyAction
|
||||
{
|
||||
public:
|
||||
CastThornsOnPartyAction(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, "thorns") {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class CastThornsOnMainTankAction : public BuffOnMainTankAction
|
||||
{
|
||||
public:
|
||||
CastThornsOnMainTankAction(PlayerbotAI* botAI) : BuffOnMainTankAction(botAI, "thorns", false) {}
|
||||
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class CastLifebloomOnMainTankAction : public BuffOnMainTankAction
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDAQ20TRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidAq20Triggers.h"
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||
#define _PLAYERBOT_RAIDAQ20STRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidAq20Strategy : public Strategy
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDBWLTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDBWLTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidBwlTriggers.h"
|
||||
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDBWLSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDBWLSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidBwlStrategy : public Strategy
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidEoETriggers.h"
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDEOESTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDEOESTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidEoEStrategy : public Strategy
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidGruulsLairTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidGruulsLairTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#define _PLAYERBOT_RAIDGRUULSLAIRSTRATEGY_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidGruulsLairStrategy : public Strategy
|
||||
{
|
||||
|
||||
1200
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.cpp
Normal file
1200
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.cpp
Normal file
File diff suppressed because it is too large
Load Diff
277
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.h
Normal file
277
src/Ai/Raid/HyjalSummit/Action/RaidHyjalSummitActions.h
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITACTIONS_H
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITACTIONS_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
// General
|
||||
|
||||
class HyjalSummitEraseTrackersAction : public Action
|
||||
{
|
||||
public:
|
||||
HyjalSummitEraseTrackersAction(
|
||||
PlayerbotAI* botAI) : Action(botAI, "hyjal summit erase trackers") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Rage Winterchill
|
||||
|
||||
class RageWinterchillMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
RageWinterchillMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "rage winterchill misdirect boss to main tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class RageWinterchillMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
RageWinterchillMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "rage winterchill main tank position boss") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class RageWinterchillSpreadRangedInCircleAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
RageWinterchillSpreadRangedInCircleAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "rage winterchill spread ranged in circle") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class RageWinterchillMeleeGetOutOfDeathAndDecayAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
RageWinterchillMeleeGetOutOfDeathAndDecayAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "rage winterchill melee get out of death and decay") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Anetheron
|
||||
|
||||
class AnetheronMisdirectBossAndInfernalsToTanksAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AnetheronMisdirectBossAndInfernalsToTanksAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron misdirect boss and infernals to tanks") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AnetheronMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AnetheronMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron main tank position boss") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AnetheronSpreadRangedInCircleAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AnetheronSpreadRangedInCircleAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "anetheron spread ranged in circle") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AnetheronBringInfernalToInfernalTankAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AnetheronBringInfernalToInfernalTankAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "anetheron bring infernal to infernal tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AnetheronFirstAssistTankPickUpInfernalsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AnetheronFirstAssistTankPickUpInfernalsAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron first assist tank pick up infernals") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AnetheronAssignDpsPriorityAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AnetheronAssignDpsPriorityAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "anetheron assign dps priority") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Kaz'rogal
|
||||
|
||||
class KazrogalMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
KazrogalMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "kaz'rogal misdirect boss to main tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class KazrogalMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
KazrogalMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "kaz'rogal main tank position boss") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class KazrogalAssistTanksMoveInFrontOfBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
KazrogalAssistTanksMoveInFrontOfBossAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "kaz'rogal assist tanks move in front of boss") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class KazrogalSpreadRangedInArcAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
KazrogalSpreadRangedInArcAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "kaz'rogal spread ranged in arc") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class KazrogalLowManaBotTakeDefensiveMeasuresAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
KazrogalLowManaBotTakeDefensiveMeasuresAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "kaz'rogal low mana bot take defensive measures") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class KazrogalCastShadowProtectionSpellAction : public Action
|
||||
{
|
||||
public:
|
||||
KazrogalCastShadowProtectionSpellAction(
|
||||
PlayerbotAI* botAI) : Action(botAI, "kaz'rogal cast shadow protection spell") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Azgalor
|
||||
|
||||
class AzgalorMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AzgalorMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor misdirect boss to main tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorMainTankPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AzgalorMainTankPositionBossAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor main tank position boss") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorWaitAtSafePositionAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AzgalorWaitAtSafePositionAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "azgalor wait at safe position") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorDisperseRangedAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AzgalorDisperseRangedAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "azgalor disperse ranged") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorMeleeGetOutOfFireAndSwapTargetsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AzgalorMeleeGetOutOfFireAndSwapTargetsAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor melee get out of fire and swap targets") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorMoveToDoomguardTankAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
AzgalorMoveToDoomguardTankAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "azgalor move to doomguard tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorFirstAssistTankPositionDoomguardAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AzgalorFirstAssistTankPositionDoomguardAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor first assist tank position doomguard") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class AzgalorRangedDpsPrioritizeDoomguardsAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
AzgalorRangedDpsPrioritizeDoomguardsAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "azgalor ranged dps prioritize doomguards") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
// Archimonde
|
||||
|
||||
class ArchimondeMisdirectBossToMainTankAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
ArchimondeMisdirectBossToMainTankAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "archimonde misdirect boss to main tank") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ArchimondeMoveBossToInitialPositionAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
ArchimondeMoveBossToInitialPositionAction(
|
||||
PlayerbotAI* botAI) : AttackAction(botAI, "archimonde move boss to initial position") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ArchimondeCastFearImmunitySpellAction : public Action
|
||||
{
|
||||
public:
|
||||
ArchimondeCastFearImmunitySpellAction(
|
||||
PlayerbotAI* botAI) : Action(botAI, "archimonde cast fear immunity spell") {}
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
bool CastFearWardOnMainTank();
|
||||
bool UseTremorTotemStrategy();
|
||||
};
|
||||
|
||||
class ArchimondeSpreadToAvoidAirBurstAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ArchimondeSpreadToAvoidAirBurstAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "archimonde spread to avoid air burst") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ArchimondeAvoidDoomfireAction : public MovementAction
|
||||
{
|
||||
public:
|
||||
ArchimondeAvoidDoomfireAction(
|
||||
PlayerbotAI* botAI) : MovementAction(botAI, "archimonde avoid doomfire") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class ArchimondeRemoveDoomfireDotAction : public Action
|
||||
{
|
||||
public:
|
||||
ArchimondeRemoveDoomfireDotAction(
|
||||
PlayerbotAI* botAI) : Action(botAI, "archimonde remove doomfire dot") {}
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidHyjalSummitMultipliers.h"
|
||||
#include "RaidHyjalSummitActions.h"
|
||||
#include "RaidHyjalSummitHelpers.h"
|
||||
#include "AiFactory.h"
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
using namespace HyjalSummitHelpers;
|
||||
|
||||
// Without this multiplier, Bloodlust/Heroism will not be available for
|
||||
// bosses because it will be used on cooldown during trash waves
|
||||
float HyjalSummitTimeBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_SHAMAN)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||
dynamic_cast<CastHeroismAction*>(action))
|
||||
{
|
||||
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
|
||||
if (archimonde && archimonde->GetHealthPct() < 90.0f)
|
||||
return 1.0f;
|
||||
|
||||
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
if (azgalor && azgalor->GetHealthPct() < 90.0f)
|
||||
return 1.0f;
|
||||
|
||||
Unit* kazrogal = AI_VALUE2(Unit*, "find target", "kaz'rogal");
|
||||
if (kazrogal && kazrogal->GetHealthPct() < 90.0f)
|
||||
return 1.0f;
|
||||
|
||||
Unit* anetheron = AI_VALUE2(Unit*, "find target", "anetheron");
|
||||
if (anetheron && anetheron->GetHealthPct() < 85.0f)
|
||||
return 1.0f;
|
||||
|
||||
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
|
||||
if (winterchill && winterchill->GetHealthPct() < 90.0f)
|
||||
return 1.0f;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Rage Winterchill
|
||||
|
||||
float RageWinterchillDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "rage winterchill"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float RageWinterchillMeleeControlAvoidanceMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsRanged(bot))
|
||||
return 1.0f;
|
||||
|
||||
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
|
||||
if (!winterchill)
|
||||
return 1.0f;
|
||||
|
||||
if (IsInDeathAndDecay(bot, DEATH_AND_DECAY_SAFE_RADIUS + 2.0f))
|
||||
{
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot) || winterchill->GetVictim() == bot)
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<RageWinterchillMeleeGetOutOfDeathAndDecayAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Anetheron
|
||||
|
||||
float AnetheronDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!botAI->IsTank(bot) || !AI_VALUE2(Unit*, "find target", "anetheron"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (bot->GetVictim() != nullptr &&
|
||||
dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float AnetheronDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "anetheron"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float AnetheronControlMisdirectionMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER ||
|
||||
!AI_VALUE2(Unit*, "find target", "anetheron"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Kaz'rogal
|
||||
|
||||
float KazrogalLowManaBotStayAwayFromGroupMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() == CLASS_WARRIOR || bot->getClass() == CLASS_ROGUE ||
|
||||
bot->getClass() == CLASS_DEATH_KNIGHT || bot->getClass() == CLASS_HUNTER)
|
||||
return 1.0f;
|
||||
|
||||
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL)
|
||||
return 1.0f;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return 1.0f;
|
||||
|
||||
if (!isBelowManaThreshold.count(bot->GetGUID()))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
(dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<KazrogalLowManaBotTakeDefensiveMeasuresAction*>(action)))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float KazrogalKeepAspectOfTheViperActiveMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER || bot->GetPower(POWER_MANA) > 4000 ||
|
||||
!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CastAspectOfTheHawkAction*>(action) ||
|
||||
dynamic_cast<CastAspectOfTheWildAction*>(action) ||
|
||||
dynamic_cast<CastAspectOfTheDragonhawkAction*>(action) ||
|
||||
dynamic_cast<CastAspectOfTheCheetahAction*>(action) ||
|
||||
dynamic_cast<CastAspectOfThePackAction*>(action) ||
|
||||
dynamic_cast<CastAspectOfTheMonkeyAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float KazrogalControlMovementMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<FleeAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (botAI->IsRanged(bot) && dynamic_cast<ReachTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Azgalor
|
||||
|
||||
float AzgalorDisableTankActionsMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (bot->GetVictim() == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
if (!botAI->IsTank(bot) || !AI_VALUE2(Unit*, "find target", "azgalor"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<TankFaceAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<TankAssistAction*>(action) || dynamic_cast<AvoidAoeAction*>(action))
|
||||
{
|
||||
if (botAI->IsMainTank(bot))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else if (botAI->IsAssistTank(bot) && (AnyGroupMemberHasDoom(bot) ||
|
||||
AI_VALUE2(Unit*, "find target", "lesser doomguard")))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float AzgalorDoomedBotPrioritizePositioningMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AttackAction*>(action) &&
|
||||
!dynamic_cast<AvoidAoeAction*>(action) &&
|
||||
!dynamic_cast<AzgalorMoveToDoomguardTankAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
float AzgalorMeleeDpsControlAvoidanceMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
if (!azgalor)
|
||||
return 1.0f;
|
||||
|
||||
constexpr float singleTickMoveAwayDist = 6.0f;
|
||||
if (IsInRainOfFire(bot, RAIN_OF_FIRE_RADIUS + singleTickMoveAwayDist))
|
||||
{
|
||||
if (dynamic_cast<AvoidAoeAction*>(action) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AzgalorMeleeGetOutOfFireAndSwapTargetsAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||
if (!mainTank || !GET_PLAYERBOT_AI(mainTank))
|
||||
return 1.0f;
|
||||
|
||||
TankPositionState tankState = GetAzgalorTankPositionState(botAI, bot);
|
||||
if ((tankState == TankPositionState::Unknown ||
|
||||
tankState == TankPositionState::MovingToTransition) &&
|
||||
dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<AzgalorWaitAtSafePositionAction*>(action))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Archimonde
|
||||
|
||||
float ArchimondeDisableCombatFormationMoveMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (!AI_VALUE2(Unit*, "find target", "archimonde"))
|
||||
return 1.0f;
|
||||
|
||||
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
|
||||
!dynamic_cast<SetBehindTargetAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
125
src/Ai/Raid/HyjalSummit/Multiplier/RaidHyjalSummitMultipliers.h
Normal file
125
src/Ai/Raid/HyjalSummit/Multiplier/RaidHyjalSummitMultipliers.h
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITMULTIPLIERS_H
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITMULTIPLIERS_H
|
||||
|
||||
#include "Multiplier.h"
|
||||
|
||||
class HyjalSummitTimeBloodlustAndHeroismMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
HyjalSummitTimeBloodlustAndHeroismMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "hyjal summit time bloodlust and heroism multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Rage Winterchill
|
||||
|
||||
class RageWinterchillDisableCombatFormationMoveMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
RageWinterchillDisableCombatFormationMoveMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "rage winterchill disable combat formation move multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class RageWinterchillMeleeControlAvoidanceMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
RageWinterchillMeleeControlAvoidanceMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "rage winterchill melee control avoidance multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Anetheron
|
||||
|
||||
class AnetheronDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AnetheronDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "anetheron disable tank actions multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AnetheronDisableCombatFormationMoveMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AnetheronDisableCombatFormationMoveMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "anetheron disable combat formation move multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AnetheronControlMisdirectionMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AnetheronControlMisdirectionMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "anetheron control misdirection multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Kaz'rogal
|
||||
|
||||
class KazrogalLowManaBotStayAwayFromGroupMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
KazrogalLowManaBotStayAwayFromGroupMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "kaz'rogal low mana bot stay away from group multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class KazrogalKeepAspectOfTheViperActiveMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
KazrogalKeepAspectOfTheViperActiveMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "kaz'rogal keep aspect of the viper active multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class KazrogalControlMovementMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
KazrogalControlMovementMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "kaz'rogal control movement multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Azgalor
|
||||
|
||||
class AzgalorDisableTankActionsMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AzgalorDisableTankActionsMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "azgalor disable tank actions multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AzgalorDoomedBotPrioritizePositioningMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AzgalorDoomedBotPrioritizePositioningMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "azgalor doomed bot prioritize positioning multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
class AzgalorMeleeDpsControlAvoidanceMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
AzgalorMeleeDpsControlAvoidanceMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "azgalor melee dps control avoidance multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
// Archimonde
|
||||
|
||||
class ArchimondeDisableCombatFormationMoveMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
ArchimondeDisableCombatFormationMoveMultiplier(
|
||||
PlayerbotAI* botAI) : Multiplier(botAI, "archimonde disable combat formation move multiplier") {}
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h
Normal file
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITACTIONCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITACTIONCONTEXT_H
|
||||
|
||||
#include "RaidHyjalSummitActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidHyjalSummitActionContext : public NamedObjectContext<Action>
|
||||
{
|
||||
public:
|
||||
RaidHyjalSummitActionContext()
|
||||
{
|
||||
// General
|
||||
creators["hyjal summit erase trackers"] =
|
||||
&RaidHyjalSummitActionContext::hyjal_summit_erase_trackers;
|
||||
|
||||
// Rage Winterchill
|
||||
creators["rage winterchill misdirect boss to main tank"] =
|
||||
&RaidHyjalSummitActionContext::rage_winterchill_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["rage winterchill main tank position boss"] =
|
||||
&RaidHyjalSummitActionContext::rage_winterchill_main_tank_position_boss;
|
||||
|
||||
creators["rage winterchill spread ranged in circle"] =
|
||||
&RaidHyjalSummitActionContext::rage_winterchill_spread_ranged_in_circle;
|
||||
|
||||
creators["rage winterchill melee get out of death and decay"] =
|
||||
&RaidHyjalSummitActionContext::rage_winterchill_melee_get_out_of_death_and_decay;
|
||||
|
||||
// Anetheron
|
||||
creators["anetheron misdirect boss and infernals to tanks"] =
|
||||
&RaidHyjalSummitActionContext::anetheron_misdirect_boss_and_infernals_to_tanks;
|
||||
|
||||
creators["anetheron main tank position boss"] =
|
||||
&RaidHyjalSummitActionContext::anetheron_main_tank_position_boss;
|
||||
|
||||
creators["anetheron spread ranged in circle"] =
|
||||
&RaidHyjalSummitActionContext::anetheron_spread_ranged_in_circle;
|
||||
|
||||
creators["anetheron bring infernal to infernal tank"] =
|
||||
&RaidHyjalSummitActionContext::anetheron_bring_infernal_to_infernal_tank;
|
||||
|
||||
creators["anetheron first assist tank pick up infernals"] =
|
||||
&RaidHyjalSummitActionContext::anetheron_first_assist_tank_pick_up_infernals;
|
||||
|
||||
creators["anetheron assign dps priority"] =
|
||||
&RaidHyjalSummitActionContext::anetheron_assign_dps_priority;
|
||||
|
||||
// Kaz'rogal
|
||||
creators["kaz'rogal misdirect boss to main tank"] =
|
||||
&RaidHyjalSummitActionContext::kazrogal_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["kaz'rogal main tank position boss"] =
|
||||
&RaidHyjalSummitActionContext::kazrogal_main_tank_position_boss;
|
||||
|
||||
creators["kaz'rogal assist tanks move in front of boss"] =
|
||||
&RaidHyjalSummitActionContext::kazrogal_assist_tanks_move_in_front_of_boss;
|
||||
|
||||
creators["kaz'rogal spread ranged in arc"] =
|
||||
&RaidHyjalSummitActionContext::kazrogal_spread_ranged_in_arc;
|
||||
|
||||
creators["kaz'rogal low mana bot take defensive measures"] =
|
||||
&RaidHyjalSummitActionContext::kazrogal_low_mana_bot_take_defensive_measures;
|
||||
|
||||
creators["kaz'rogal cast shadow protection spell"] =
|
||||
&RaidHyjalSummitActionContext::kazrogal_cast_shadow_protection_spell;
|
||||
|
||||
// Azgalor
|
||||
creators["azgalor misdirect boss to main tank"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["azgalor main tank position boss"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_main_tank_position_boss;
|
||||
|
||||
creators["azgalor wait at safe position"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_wait_at_safe_position;
|
||||
|
||||
creators["azgalor disperse ranged"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_disperse_ranged;
|
||||
|
||||
creators["azgalor melee get out of fire and swap targets"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_melee_get_out_of_fire_and_swap_targets;
|
||||
|
||||
creators["azgalor move to doomguard tank"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_move_to_doomguard_tank;
|
||||
|
||||
creators["azgalor first assist tank position doomguard"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_first_assist_tank_position_doomguard;
|
||||
|
||||
creators["azgalor ranged dps prioritize doomguards"] =
|
||||
&RaidHyjalSummitActionContext::azgalor_ranged_dps_prioritize_doomguards;
|
||||
|
||||
// Archimonde
|
||||
creators["archimonde misdirect boss to main tank"] =
|
||||
&RaidHyjalSummitActionContext::archimonde_misdirect_boss_to_main_tank;
|
||||
|
||||
creators["archimonde move boss to initial position"] =
|
||||
&RaidHyjalSummitActionContext::archimonde_move_boss_to_initial_position;
|
||||
|
||||
creators["archimonde cast fear immunity spell"] =
|
||||
&RaidHyjalSummitActionContext::archimonde_cast_fear_immunity_spell;
|
||||
|
||||
creators["archimonde spread to avoid air burst"] =
|
||||
&RaidHyjalSummitActionContext::archimonde_spread_to_avoid_air_burst;
|
||||
|
||||
creators["archimonde avoid doomfire"] =
|
||||
&RaidHyjalSummitActionContext::archimonde_avoid_doomfire;
|
||||
|
||||
creators["archimonde remove doomfire dot"] =
|
||||
&RaidHyjalSummitActionContext::archimonde_remove_doomfire_dot;
|
||||
}
|
||||
|
||||
private:
|
||||
// General
|
||||
static Action* hyjal_summit_erase_trackers(
|
||||
PlayerbotAI* botAI) { return new HyjalSummitEraseTrackersAction(botAI); }
|
||||
|
||||
// Rage Winterchill
|
||||
static Action* rage_winterchill_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* rage_winterchill_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* rage_winterchill_spread_ranged_in_circle(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillSpreadRangedInCircleAction(botAI); }
|
||||
|
||||
static Action* rage_winterchill_melee_get_out_of_death_and_decay(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillMeleeGetOutOfDeathAndDecayAction(botAI); }
|
||||
|
||||
// Anetheron
|
||||
static Action* anetheron_misdirect_boss_and_infernals_to_tanks(
|
||||
PlayerbotAI* botAI) { return new AnetheronMisdirectBossAndInfernalsToTanksAction(botAI); }
|
||||
|
||||
static Action* anetheron_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new AnetheronMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* anetheron_spread_ranged_in_circle(
|
||||
PlayerbotAI* botAI) { return new AnetheronSpreadRangedInCircleAction(botAI); }
|
||||
|
||||
static Action* anetheron_bring_infernal_to_infernal_tank(
|
||||
PlayerbotAI* botAI) { return new AnetheronBringInfernalToInfernalTankAction(botAI); }
|
||||
|
||||
static Action* anetheron_first_assist_tank_pick_up_infernals(
|
||||
PlayerbotAI* botAI) { return new AnetheronFirstAssistTankPickUpInfernalsAction(botAI); }
|
||||
|
||||
static Action* anetheron_assign_dps_priority(
|
||||
PlayerbotAI* botAI) { return new AnetheronAssignDpsPriorityAction(botAI); }
|
||||
|
||||
// Kaz'rogal
|
||||
static Action* kazrogal_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new KazrogalMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* kazrogal_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new KazrogalMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* kazrogal_assist_tanks_move_in_front_of_boss(
|
||||
PlayerbotAI* botAI) { return new KazrogalAssistTanksMoveInFrontOfBossAction(botAI); }
|
||||
|
||||
static Action* kazrogal_spread_ranged_in_arc(
|
||||
PlayerbotAI* botAI) { return new KazrogalSpreadRangedInArcAction(botAI); }
|
||||
|
||||
static Action* kazrogal_low_mana_bot_take_defensive_measures(
|
||||
PlayerbotAI* botAI) { return new KazrogalLowManaBotTakeDefensiveMeasuresAction(botAI); }
|
||||
|
||||
static Action* kazrogal_cast_shadow_protection_spell(
|
||||
PlayerbotAI* botAI) { return new KazrogalCastShadowProtectionSpellAction(botAI); }
|
||||
|
||||
// Azgalor
|
||||
static Action* azgalor_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new AzgalorMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* azgalor_main_tank_position_boss(
|
||||
PlayerbotAI* botAI) { return new AzgalorMainTankPositionBossAction(botAI); }
|
||||
|
||||
static Action* azgalor_wait_at_safe_position(
|
||||
PlayerbotAI* botAI) { return new AzgalorWaitAtSafePositionAction(botAI); }
|
||||
|
||||
static Action* azgalor_disperse_ranged(
|
||||
PlayerbotAI* botAI) { return new AzgalorDisperseRangedAction(botAI); }
|
||||
|
||||
static Action* azgalor_melee_get_out_of_fire_and_swap_targets(
|
||||
PlayerbotAI* botAI) { return new AzgalorMeleeGetOutOfFireAndSwapTargetsAction(botAI); }
|
||||
|
||||
static Action* azgalor_move_to_doomguard_tank(
|
||||
PlayerbotAI* botAI) { return new AzgalorMoveToDoomguardTankAction(botAI); }
|
||||
|
||||
static Action* azgalor_first_assist_tank_position_doomguard(
|
||||
PlayerbotAI* botAI) { return new AzgalorFirstAssistTankPositionDoomguardAction(botAI); }
|
||||
|
||||
static Action* azgalor_ranged_dps_prioritize_doomguards(
|
||||
PlayerbotAI* botAI) { return new AzgalorRangedDpsPrioritizeDoomguardsAction(botAI); }
|
||||
|
||||
// Archimonde
|
||||
static Action* archimonde_misdirect_boss_to_main_tank(
|
||||
PlayerbotAI* botAI) { return new ArchimondeMisdirectBossToMainTankAction(botAI); }
|
||||
|
||||
static Action* archimonde_move_boss_to_initial_position(
|
||||
PlayerbotAI* botAI) { return new ArchimondeMoveBossToInitialPositionAction(botAI); }
|
||||
|
||||
static Action* archimonde_cast_fear_immunity_spell(
|
||||
PlayerbotAI* botAI) { return new ArchimondeCastFearImmunitySpellAction(botAI); }
|
||||
|
||||
static Action* archimonde_spread_to_avoid_air_burst(
|
||||
PlayerbotAI* botAI) { return new ArchimondeSpreadToAvoidAirBurstAction(botAI); }
|
||||
|
||||
static Action* archimonde_avoid_doomfire(
|
||||
PlayerbotAI* botAI) { return new ArchimondeAvoidDoomfireAction(botAI); }
|
||||
|
||||
static Action* archimonde_remove_doomfire_dot(
|
||||
PlayerbotAI* botAI) { return new ArchimondeRemoveDoomfireDotAction(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h
Normal file
218
src/Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidHyjalSummitTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidHyjalSummitTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
public:
|
||||
RaidHyjalSummitTriggerContext()
|
||||
{
|
||||
// General
|
||||
creators["hyjal summit bot is not in combat"] =
|
||||
&RaidHyjalSummitTriggerContext::hyjal_summit_bot_is_not_in_combat;
|
||||
|
||||
// Rage Winterchill
|
||||
creators["rage winterchill pulling boss"] =
|
||||
&RaidHyjalSummitTriggerContext::rage_winterchill_pulling_boss;
|
||||
|
||||
creators["rage winterchill boss engaged by main tank"] =
|
||||
&RaidHyjalSummitTriggerContext::rage_winterchill_boss_engaged_by_main_tank;
|
||||
|
||||
creators["rage winterchill boss casts death and decay on ranged"] =
|
||||
&RaidHyjalSummitTriggerContext::rage_winterchill_boss_casts_death_and_decay_on_ranged;
|
||||
|
||||
creators["rage winterchill melee is standing in death and decay"] =
|
||||
&RaidHyjalSummitTriggerContext::rage_winterchill_melee_is_standing_in_death_and_decay;
|
||||
|
||||
// Anetheron
|
||||
creators["anetheron pulling boss or infernal"] =
|
||||
&RaidHyjalSummitTriggerContext::anetheron_pulling_boss_or_infernal;
|
||||
|
||||
creators["anetheron boss engaged by main tank"] =
|
||||
&RaidHyjalSummitTriggerContext::anetheron_boss_engaged_by_main_tank;
|
||||
|
||||
creators["anetheron boss casts carrion swarm"] =
|
||||
&RaidHyjalSummitTriggerContext::anetheron_boss_casts_carrion_swarm;
|
||||
|
||||
creators["anetheron bot is targeted by infernal"] =
|
||||
&RaidHyjalSummitTriggerContext::anetheron_bot_is_targeted_by_infernal;
|
||||
|
||||
creators["anetheron infernals need to be kept away from raid"] =
|
||||
&RaidHyjalSummitTriggerContext::anetheron_infernals_need_to_be_kept_away_from_raid;
|
||||
|
||||
creators["anetheron infernals continue to spawn"] =
|
||||
&RaidHyjalSummitTriggerContext::anetheron_infernals_continue_to_spawn;
|
||||
|
||||
// Kaz'rogal
|
||||
creators["kaz'rogal pulling boss"] =
|
||||
&RaidHyjalSummitTriggerContext::kazrogal_pulling_boss;
|
||||
|
||||
creators["kaz'rogal boss engaged by main tank"] =
|
||||
&RaidHyjalSummitTriggerContext::kazrogal_boss_engaged_by_main_tank;
|
||||
|
||||
creators["kaz'rogal boss engaged by assist tanks"] =
|
||||
&RaidHyjalSummitTriggerContext::kazrogal_boss_engaged_by_assist_tanks;
|
||||
|
||||
creators["kaz'rogal bot is low on mana"] =
|
||||
&RaidHyjalSummitTriggerContext::kazrogal_bot_is_low_on_mana;
|
||||
|
||||
creators["kaz'rogal low mana bots need escape path"] =
|
||||
&RaidHyjalSummitTriggerContext::kazrogal_low_mana_bots_need_escape_path;
|
||||
|
||||
creators["kaz'rogal mark deals shadow damage"] =
|
||||
&RaidHyjalSummitTriggerContext::kazrogal_mark_deals_shadow_damage;
|
||||
|
||||
// Azgalor
|
||||
creators["azgalor pulling boss"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_pulling_boss;
|
||||
|
||||
creators["azgalor boss engaged by main tank"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_boss_engaged_by_main_tank;
|
||||
|
||||
creators["azgalor main tank is positioning boss"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_main_tank_is_positioning_boss;
|
||||
|
||||
creators["azgalor boss engaged by ranged"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_boss_engaged_by_ranged;
|
||||
|
||||
creators["azgalor boss casts rain of fire on melee"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_boss_casts_rain_of_fire_on_melee;
|
||||
|
||||
creators["azgalor bot is doomed"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_bot_is_doomed;
|
||||
|
||||
creators["azgalor doomguards must be controlled"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_doomguards_must_be_controlled;
|
||||
|
||||
creators["azgalor doomguards must die"] =
|
||||
&RaidHyjalSummitTriggerContext::azgalor_doomguards_must_die;
|
||||
|
||||
// Archimonde
|
||||
creators["archimonde pulling boss"] =
|
||||
&RaidHyjalSummitTriggerContext::archimonde_pulling_boss;
|
||||
|
||||
creators["archimonde boss engaged by main tank"] =
|
||||
&RaidHyjalSummitTriggerContext::archimonde_boss_engaged_by_main_tank;
|
||||
|
||||
creators["archimonde boss casts fear"] =
|
||||
&RaidHyjalSummitTriggerContext::archimonde_boss_casts_fear;
|
||||
|
||||
creators["archimonde boss casts air burst"] =
|
||||
&RaidHyjalSummitTriggerContext::archimonde_boss_casts_air_burst;
|
||||
|
||||
creators["archimonde boss summoned doomfire"] =
|
||||
&RaidHyjalSummitTriggerContext::archimonde_boss_summoned_doomfire;
|
||||
|
||||
creators["archimonde bot stood in doomfire"] =
|
||||
&RaidHyjalSummitTriggerContext::archimonde_bot_stood_in_doomfire;
|
||||
}
|
||||
|
||||
private:
|
||||
// General
|
||||
static Trigger* hyjal_summit_bot_is_not_in_combat(
|
||||
PlayerbotAI* botAI) { return new HyjalSummitBotIsNotInCombatTrigger(botAI); }
|
||||
|
||||
// Rage Winterchill
|
||||
static Trigger* rage_winterchill_pulling_boss(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillPullingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* rage_winterchill_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* rage_winterchill_boss_casts_death_and_decay_on_ranged(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillBossCastsDeathAndDecayOnRangedTrigger(botAI); }
|
||||
|
||||
static Trigger* rage_winterchill_melee_is_standing_in_death_and_decay(
|
||||
PlayerbotAI* botAI) { return new RageWinterchillMeleeIsStandingInDeathAndDecayTrigger(botAI); }
|
||||
|
||||
// Anetheron
|
||||
static Trigger* anetheron_pulling_boss_or_infernal(
|
||||
PlayerbotAI* botAI) { return new AnetheronPullingBossOrInfernalTrigger(botAI); }
|
||||
|
||||
static Trigger* anetheron_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new AnetheronBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* anetheron_boss_casts_carrion_swarm(
|
||||
PlayerbotAI* botAI) { return new AnetheronBossCastsCarrionSwarmTrigger(botAI); }
|
||||
|
||||
static Trigger* anetheron_bot_is_targeted_by_infernal(
|
||||
PlayerbotAI* botAI) { return new AnetheronBotIsTargetedByInfernalTrigger(botAI); }
|
||||
|
||||
static Trigger* anetheron_infernals_need_to_be_kept_away_from_raid(
|
||||
PlayerbotAI* botAI) { return new AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger(botAI); }
|
||||
|
||||
static Trigger* anetheron_infernals_continue_to_spawn(
|
||||
PlayerbotAI* botAI) { return new AnetheronInfernalsContinueToSpawnTrigger(botAI); }
|
||||
|
||||
// Kaz'rogal
|
||||
static Trigger* kazrogal_pulling_boss(
|
||||
PlayerbotAI* botAI) { return new KazrogalPullingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* kazrogal_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new KazrogalBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* kazrogal_boss_engaged_by_assist_tanks(
|
||||
PlayerbotAI* botAI) { return new KazrogalBossEngagedByAssistTanksTrigger(botAI); }
|
||||
|
||||
static Trigger* kazrogal_low_mana_bots_need_escape_path(
|
||||
PlayerbotAI* botAI) { return new KazrogalLowManaBotsNeedEscapePathTrigger(botAI); }
|
||||
|
||||
static Trigger* kazrogal_bot_is_low_on_mana(
|
||||
PlayerbotAI* botAI) { return new KazrogalBotIsLowOnManaTrigger(botAI); }
|
||||
|
||||
static Trigger* kazrogal_mark_deals_shadow_damage(
|
||||
PlayerbotAI* botAI) { return new KazrogalMarkDealsShadowDamageTrigger(botAI); }
|
||||
|
||||
// Azgalor
|
||||
static Trigger* azgalor_pulling_boss(
|
||||
PlayerbotAI* botAI) { return new AzgalorPullingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new AzgalorBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_main_tank_is_positioning_boss(
|
||||
PlayerbotAI* botAI) { return new AzgalorMainTankIsPositioningBossTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_boss_engaged_by_ranged(
|
||||
PlayerbotAI* botAI) { return new AzgalorBossEngagedByRangedTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_boss_casts_rain_of_fire_on_melee(
|
||||
PlayerbotAI* botAI) { return new AzgalorBossCastsRainOfFireOnMeleeTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_bot_is_doomed(
|
||||
PlayerbotAI* botAI) { return new AzgalorBotIsDoomedTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_doomguards_must_be_controlled(
|
||||
PlayerbotAI* botAI) { return new AzgalorDoomguardsMustBeControlledTrigger(botAI); }
|
||||
|
||||
static Trigger* azgalor_doomguards_must_die(
|
||||
PlayerbotAI* botAI) { return new AzgalorDoomguardsMustDieTrigger(botAI); }
|
||||
|
||||
// Archimonde
|
||||
static Trigger* archimonde_pulling_boss(
|
||||
PlayerbotAI* botAI) { return new ArchimondePullingBossTrigger(botAI); }
|
||||
|
||||
static Trigger* archimonde_boss_engaged_by_main_tank(
|
||||
PlayerbotAI* botAI) { return new ArchimondeBossEngagedByMainTankTrigger(botAI); }
|
||||
|
||||
static Trigger* archimonde_boss_casts_fear(
|
||||
PlayerbotAI* botAI) { return new ArchimondeBossCastsFearTrigger(botAI); }
|
||||
|
||||
static Trigger* archimonde_boss_casts_air_burst(
|
||||
PlayerbotAI* botAI) { return new ArchimondeBossCastsAirBurstTrigger(botAI); }
|
||||
|
||||
static Trigger* archimonde_boss_summoned_doomfire(
|
||||
PlayerbotAI* botAI) { return new ArchimondeBossSummonedDoomfireTrigger(botAI); }
|
||||
|
||||
static Trigger* archimonde_bot_stood_in_doomfire(
|
||||
PlayerbotAI* botAI) { return new ArchimondeBotStoodInDoomfireTrigger(botAI); }
|
||||
};
|
||||
|
||||
#endif
|
||||
137
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.cpp
Normal file
137
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidHyjalSummitStrategy.h"
|
||||
#include "RaidHyjalSummitMultipliers.h"
|
||||
|
||||
void RaidHyjalSummitStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
// General
|
||||
triggers.push_back(new TriggerNode("hyjal summit bot is not in combat", {
|
||||
NextAction("hyjal summit erase trackers", ACTION_EMERGENCY + 11) }));
|
||||
|
||||
// Rage Winterchill
|
||||
triggers.push_back(new TriggerNode("rage winterchill pulling boss", {
|
||||
NextAction("rage winterchill misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("rage winterchill boss engaged by main tank", {
|
||||
NextAction("rage winterchill main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("rage winterchill boss casts death and decay on ranged", {
|
||||
NextAction("rage winterchill spread ranged in circle", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("rage winterchill melee is standing in death and decay", {
|
||||
NextAction("rage winterchill melee get out of death and decay", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
// Anetheron
|
||||
triggers.push_back(new TriggerNode("anetheron pulling boss or infernal", {
|
||||
NextAction("anetheron misdirect boss and infernals to tanks", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("anetheron boss engaged by main tank", {
|
||||
NextAction("anetheron main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("anetheron boss casts carrion swarm", {
|
||||
NextAction("anetheron spread ranged in circle", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("anetheron bot is targeted by infernal", {
|
||||
NextAction("anetheron bring infernal to infernal tank", ACTION_EMERGENCY + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("anetheron infernals need to be kept away from raid", {
|
||||
NextAction("anetheron first assist tank pick up infernals", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("anetheron infernals continue to spawn", {
|
||||
NextAction("anetheron assign dps priority", ACTION_RAID + 1) }));
|
||||
|
||||
// Kaz'rogal
|
||||
triggers.push_back(new TriggerNode("kaz'rogal pulling boss", {
|
||||
NextAction("kaz'rogal misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("kaz'rogal boss engaged by main tank", {
|
||||
NextAction("kaz'rogal main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("kaz'rogal boss engaged by assist tanks", {
|
||||
NextAction("kaz'rogal assist tanks move in front of boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("kaz'rogal low mana bots need escape path", {
|
||||
NextAction("kaz'rogal spread ranged in arc", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("kaz'rogal bot is low on mana", {
|
||||
NextAction("kaz'rogal low mana bot take defensive measures", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("kaz'rogal mark deals shadow damage", {
|
||||
NextAction("kaz'rogal cast shadow protection spell", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
// Azgalor
|
||||
triggers.push_back(new TriggerNode("azgalor pulling boss", {
|
||||
NextAction("azgalor misdirect boss to main tank", ACTION_RAID + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor boss engaged by main tank", {
|
||||
NextAction("azgalor main tank position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor main tank is positioning boss", {
|
||||
NextAction("azgalor wait at safe position", ACTION_EMERGENCY + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor boss engaged by ranged", {
|
||||
NextAction("azgalor disperse ranged", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor boss casts rain of fire on melee", {
|
||||
NextAction("azgalor melee get out of fire and swap targets", ACTION_EMERGENCY + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor bot is doomed", {
|
||||
NextAction("azgalor move to doomguard tank", ACTION_EMERGENCY + 3) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor doomguards must be controlled", {
|
||||
NextAction("azgalor first assist tank position doomguard", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("azgalor doomguards must die", {
|
||||
NextAction("azgalor ranged dps prioritize doomguards", ACTION_RAID + 1) }));
|
||||
|
||||
// Archimonde
|
||||
triggers.push_back(new TriggerNode("archimonde pulling boss", {
|
||||
NextAction("archimonde misdirect boss to main tank", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("archimonde boss engaged by main tank", {
|
||||
NextAction("archimonde move boss to initial position", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("archimonde boss casts fear", {
|
||||
NextAction("archimonde cast fear immunity spell", ACTION_RAID + 2) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("archimonde boss casts air burst", {
|
||||
NextAction("archimonde spread to avoid air burst", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("archimonde boss summoned doomfire", {
|
||||
NextAction("archimonde avoid doomfire", ACTION_EMERGENCY + 6) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("archimonde bot stood in doomfire", {
|
||||
NextAction("archimonde remove doomfire dot", ACTION_EMERGENCY + 7) }));
|
||||
}
|
||||
|
||||
void RaidHyjalSummitStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
// Trash
|
||||
multipliers.push_back(new HyjalSummitTimeBloodlustAndHeroismMultiplier(botAI));
|
||||
|
||||
// Rage Winterchill
|
||||
multipliers.push_back(new RageWinterchillDisableCombatFormationMoveMultiplier(botAI));
|
||||
multipliers.push_back(new RageWinterchillMeleeControlAvoidanceMultiplier(botAI));
|
||||
|
||||
// Anetheron
|
||||
multipliers.push_back(new AnetheronDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new AnetheronDisableCombatFormationMoveMultiplier(botAI));
|
||||
multipliers.push_back(new AnetheronControlMisdirectionMultiplier(botAI));
|
||||
|
||||
// Kaz'rogal
|
||||
multipliers.push_back(new KazrogalLowManaBotStayAwayFromGroupMultiplier(botAI));
|
||||
multipliers.push_back(new KazrogalKeepAspectOfTheViperActiveMultiplier(botAI));
|
||||
multipliers.push_back(new KazrogalControlMovementMultiplier(botAI));
|
||||
|
||||
// Azgalor
|
||||
multipliers.push_back(new AzgalorDisableTankActionsMultiplier(botAI));
|
||||
multipliers.push_back(new AzgalorDoomedBotPrioritizePositioningMultiplier(botAI));
|
||||
multipliers.push_back(new AzgalorMeleeDpsControlAvoidanceMultiplier(botAI));
|
||||
|
||||
// Archimonde
|
||||
multipliers.push_back(new ArchimondeDisableCombatFormationMoveMultiplier(botAI));
|
||||
}
|
||||
22
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.h
Normal file
22
src/Ai/Raid/HyjalSummit/Strategy/RaidHyjalSummitStrategy.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITSTRATEGY_H_
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidHyjalSummitStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidHyjalSummitStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||
|
||||
std::string const getName() override { return "hyjal"; }
|
||||
|
||||
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
357
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.cpp
Normal file
357
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.cpp
Normal file
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidHyjalSummitTriggers.h"
|
||||
#include "RaidHyjalSummitHelpers.h"
|
||||
#include "RaidHyjalSummitActions.h"
|
||||
#include "AiFactory.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace HyjalSummitHelpers;
|
||||
|
||||
// General
|
||||
|
||||
bool HyjalSummitBotIsNotInCombatTrigger::IsActive()
|
||||
{
|
||||
return !bot->IsInCombat() && bot->GetMapId() == HYJAL_SUMMIT_MAP_ID;
|
||||
}
|
||||
|
||||
// Rage Winterchill
|
||||
|
||||
bool RageWinterchillPullingBossTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
|
||||
return winterchill && winterchill->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool RageWinterchillBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsMainTank(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "rage winterchill");
|
||||
}
|
||||
|
||||
bool RageWinterchillBossCastsDeathAndDecayOnRangedTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsRanged(bot) &&
|
||||
AI_VALUE2(Unit*, "find target", "rage winterchill");
|
||||
}
|
||||
|
||||
bool RageWinterchillMeleeIsStandingInDeathAndDecayTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* winterchill = AI_VALUE2(Unit*, "find target", "rage winterchill");
|
||||
if (!winterchill || winterchill->GetVictim() == bot)
|
||||
return false;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
return IsInDeathAndDecay(bot, DEATH_AND_DECAY_SAFE_RADIUS);
|
||||
}
|
||||
|
||||
// Anetheron
|
||||
|
||||
bool AnetheronPullingBossOrInfernalTrigger::IsActive()
|
||||
{
|
||||
return bot->getClass() == CLASS_HUNTER &&
|
||||
AI_VALUE2(Unit*, "find target", "anetheron");
|
||||
}
|
||||
|
||||
bool AnetheronBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "anetheron");
|
||||
}
|
||||
|
||||
bool AnetheronBossCastsCarrionSwarmTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMelee(bot))
|
||||
return false;
|
||||
|
||||
Unit* anetheron = AI_VALUE2(Unit*, "find target", "anetheron");
|
||||
if (!anetheron)
|
||||
return false;
|
||||
|
||||
return GetInfernoTarget(anetheron) != bot;
|
||||
}
|
||||
|
||||
bool AnetheronBotIsTargetedByInfernalTrigger::IsActive()
|
||||
{
|
||||
Unit* anetheron = AI_VALUE2(Unit*, "find target", "anetheron");
|
||||
if (!anetheron || botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
return GetInfernoTarget(anetheron) == bot;
|
||||
}
|
||||
|
||||
bool AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsAssistTankOfIndex(bot, 0, true) &&
|
||||
AI_VALUE2(Unit*, "find target", "towering infernal");
|
||||
}
|
||||
|
||||
bool AnetheronInfernalsContinueToSpawnTrigger::IsActive()
|
||||
{
|
||||
return !botAI->IsTank(bot) && AI_VALUE2(Unit*, "find target", "anetheron");
|
||||
}
|
||||
|
||||
// Kaz'rogal
|
||||
|
||||
bool KazrogalPullingBossTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* kazrogal = AI_VALUE2(Unit*, "find target", "kaz'rogal");
|
||||
return kazrogal && kazrogal->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool KazrogalBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "kaz'rogal");
|
||||
}
|
||||
|
||||
bool KazrogalBossEngagedByAssistTanksTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsAssistTank(bot))
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return false;
|
||||
|
||||
return bot->GetPower(POWER_MANA) > 3000;
|
||||
}
|
||||
|
||||
bool KazrogalLowManaBotsNeedEscapePathTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() == CLASS_WARRIOR || bot->getClass() == CLASS_ROGUE ||
|
||||
bot->getClass() == CLASS_DEATH_KNIGHT)
|
||||
return false;
|
||||
|
||||
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return false;
|
||||
|
||||
if (bot->getClass() == CLASS_HUNTER)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (bot->GetPower(POWER_MANA) > 4000)
|
||||
{
|
||||
isBelowManaThreshold.erase(bot->GetGUID());
|
||||
if (botAI->IsMelee(bot))
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KazrogalBotIsLowOnManaTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() == CLASS_WARRIOR || bot->getClass() == CLASS_ROGUE ||
|
||||
bot->getClass() == CLASS_DEATH_KNIGHT)
|
||||
return false;
|
||||
|
||||
uint8 tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return false;
|
||||
|
||||
if (botAI->HasAnyAuraOf(bot, "ice block", "divine shield", nullptr))
|
||||
return false;
|
||||
|
||||
if (isBelowManaThreshold.count(bot->GetGUID()) ||
|
||||
bot->GetPower(POWER_MANA) <= 3200)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KazrogalMarkDealsShadowDamageTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_PALADIN && bot->getClass() != CLASS_WARLOCK)
|
||||
return false;
|
||||
|
||||
if (!AI_VALUE2(Unit*, "find target", "kaz'rogal"))
|
||||
return false;
|
||||
|
||||
if (bot->getClass() == CLASS_PALADIN &&
|
||||
(botAI->HasAura("shadow resistance aura", bot) ||
|
||||
botAI->HasAura("prayer of shadow protection", bot) ||
|
||||
botAI->HasAura("shadow protection", bot)))
|
||||
return false;
|
||||
|
||||
return bot->HasAura(
|
||||
static_cast<uint32>(HyjalSummitSpells::SPELL_MARK_OF_KAZROGAL));
|
||||
}
|
||||
|
||||
// Azgalor
|
||||
|
||||
bool AzgalorPullingBossTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
return azgalor && azgalor->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool AzgalorBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsMainTank(bot) && AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
}
|
||||
|
||||
bool AzgalorMainTankIsPositioningBossTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsRanged(bot))
|
||||
return false;
|
||||
|
||||
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
if (!azgalor || azgalor->GetVictim() == bot)
|
||||
return false;
|
||||
|
||||
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||
if (!mainTank || !GET_PLAYERBOT_AI(mainTank) || botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
TankPositionState tankState = GetAzgalorTankPositionState(botAI, bot);
|
||||
return tankState == TankPositionState::Unknown ||
|
||||
tankState == TankPositionState::MovingToTransition;
|
||||
}
|
||||
|
||||
bool AzgalorBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsMelee(bot))
|
||||
return false;
|
||||
|
||||
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
return azgalor && azgalor->GetVictim() != bot &&
|
||||
!bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM));
|
||||
}
|
||||
|
||||
bool AzgalorBossCastsRainOfFireOnMeleeTrigger::IsActive()
|
||||
{
|
||||
if (botAI->IsRanged(bot) || botAI->IsTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* azgalor = AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
if (!azgalor || azgalor->GetVictim() == bot ||
|
||||
bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
|
||||
return false;
|
||||
|
||||
return IsInRainOfFire(bot, RAIN_OF_FIRE_RADIUS);
|
||||
}
|
||||
|
||||
bool AzgalorBotIsDoomedTrigger::IsActive()
|
||||
{
|
||||
return bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM));
|
||||
}
|
||||
|
||||
bool AzgalorDoomguardsMustBeControlledTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsAssistTank(bot) ||
|
||||
!AI_VALUE2(Unit*, "find target", "azgalor"))
|
||||
return false;
|
||||
|
||||
if (botAI->IsAssistTankOfIndex(bot, 0, true))
|
||||
{
|
||||
return AI_VALUE2(Unit*, "find target", "lesser doomguard") ||
|
||||
AnyGroupMemberHasDoom(bot);
|
||||
}
|
||||
|
||||
if (botAI->IsAssistTankOfIndex(bot, 1, true))
|
||||
{
|
||||
// Trigger for second assist tank only if first assist tank has Doom
|
||||
Player* firstAssistTank = GetGroupAssistTank(botAI, bot, 0);
|
||||
if (firstAssistTank &&
|
||||
!firstAssistTank->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
|
||||
return false;
|
||||
|
||||
return AI_VALUE2(Unit*, "find target", "lesser doomguard") ||
|
||||
AnyGroupMemberHasDoom(bot);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AzgalorDoomguardsMustDieTrigger::IsActive()
|
||||
{
|
||||
return botAI->IsRangedDps(bot) && AI_VALUE2(Unit*, "find target", "azgalor");
|
||||
}
|
||||
|
||||
// Archimonde
|
||||
|
||||
bool ArchimondePullingBossTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_HUNTER)
|
||||
return false;
|
||||
|
||||
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
|
||||
return archimonde && archimonde->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool ArchimondeBossEngagedByMainTankTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsMainTank(bot))
|
||||
return false;
|
||||
|
||||
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
|
||||
return archimonde && archimonde->GetHealthPct() > 95.0f;
|
||||
}
|
||||
|
||||
bool ArchimondeBossCastsFearTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_PRIEST &&
|
||||
bot->getClass() != CLASS_SHAMAN)
|
||||
return false;
|
||||
|
||||
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
|
||||
return archimonde && archimonde->GetHealthPct() > 10.0f;
|
||||
}
|
||||
|
||||
bool ArchimondeBossCastsAirBurstTrigger::IsActive()
|
||||
{
|
||||
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
|
||||
if (!archimonde || archimonde->GetHealthPct() <= 10.0f ||
|
||||
archimonde->GetVictim() == bot)
|
||||
return false;
|
||||
|
||||
return !botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
bool ArchimondeBossSummonedDoomfireTrigger::IsActive()
|
||||
{
|
||||
Unit* archimonde = AI_VALUE2(Unit*, "find target", "archimonde");
|
||||
if (!archimonde || archimonde->GetHealthPct() <= 10.0f)
|
||||
return false;
|
||||
|
||||
// If I don't make an exception, bots actually refuse to enter the
|
||||
// Doomfire even when feared
|
||||
return !bot->HasAura(
|
||||
static_cast<uint32>(HyjalSummitSpells::SPELL_ARCHIMONDE_FEAR));
|
||||
}
|
||||
|
||||
bool ArchimondeBotStoodInDoomfireTrigger::IsActive()
|
||||
{
|
||||
if (bot->getClass() != CLASS_MAGE && bot->getClass() != CLASS_ROGUE &&
|
||||
bot->getClass() != CLASS_PALADIN)
|
||||
return false;
|
||||
|
||||
return bot->GetHealthPct() < 40.0f &&
|
||||
(bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOMFIRE)) ||
|
||||
bot->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOMFIRE_DOT)));
|
||||
}
|
||||
271
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.h
Normal file
271
src/Ai/Raid/HyjalSummit/Trigger/RaidHyjalSummitTriggers.h
Normal file
@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITTRIGGERS_H
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITTRIGGERS_H
|
||||
|
||||
#include "Trigger.h"
|
||||
|
||||
// General
|
||||
|
||||
class HyjalSummitBotIsNotInCombatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
HyjalSummitBotIsNotInCombatTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "hyjal summit bot is not in combat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Rage Winterchill
|
||||
|
||||
class RageWinterchillPullingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RageWinterchillPullingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill pulling boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class RageWinterchillBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RageWinterchillBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class RageWinterchillBossCastsDeathAndDecayOnRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RageWinterchillBossCastsDeathAndDecayOnRangedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill boss casts death and decay on ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class RageWinterchillMeleeIsStandingInDeathAndDecayTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
RageWinterchillMeleeIsStandingInDeathAndDecayTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "rage winterchill melee is standing in death and decay") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Anetheron
|
||||
|
||||
class AnetheronPullingBossOrInfernalTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnetheronPullingBossOrInfernalTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "anetheron pulling boss or infernal") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AnetheronBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnetheronBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "anetheron boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AnetheronBossCastsCarrionSwarmTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnetheronBossCastsCarrionSwarmTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "anetheron boss casts carrion swarm") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AnetheronBotIsTargetedByInfernalTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnetheronBotIsTargetedByInfernalTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "anetheron bot is targeted by infernal") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnetheronInfernalsNeedToBeKeptAwayFromRaidTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "anetheron infernals need to be kept away from raid") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AnetheronInfernalsContinueToSpawnTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AnetheronInfernalsContinueToSpawnTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "anetheron infernals continue to spawn") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Kaz'rogal
|
||||
|
||||
class KazrogalPullingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KazrogalPullingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal pulling boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KazrogalBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KazrogalBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KazrogalBossEngagedByAssistTanksTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KazrogalBossEngagedByAssistTanksTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal boss engaged by assist tanks") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KazrogalLowManaBotsNeedEscapePathTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KazrogalLowManaBotsNeedEscapePathTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal low mana bots need escape path") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KazrogalBotIsLowOnManaTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KazrogalBotIsLowOnManaTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal bot is low on mana") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class KazrogalMarkDealsShadowDamageTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
KazrogalMarkDealsShadowDamageTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "kaz'rogal mark deals shadow damage") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Azgalor
|
||||
|
||||
class AzgalorPullingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorPullingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor pulling boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorMainTankIsPositioningBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorMainTankIsPositioningBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor main tank is positioning boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorBossEngagedByRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorBossEngagedByRangedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor boss engaged by ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorBossCastsRainOfFireOnMeleeTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorBossCastsRainOfFireOnMeleeTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor boss casts rain of fire on melee") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorBotIsDoomedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorBotIsDoomedTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor bot is doomed") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorDoomguardsMustBeControlledTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorDoomguardsMustBeControlledTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor doomguards must be controlled") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AzgalorDoomguardsMustDieTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
AzgalorDoomguardsMustDieTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "azgalor doomguards must die") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
// Archimonde
|
||||
|
||||
class ArchimondePullingBossTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArchimondePullingBossTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "archimonde pulling boss") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ArchimondeBossEngagedByMainTankTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArchimondeBossEngagedByMainTankTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss engaged by main tank") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ArchimondeBossCastsFearTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArchimondeBossCastsFearTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss casts fear") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ArchimondeBossCastsAirBurstTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArchimondeBossCastsAirBurstTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss casts air burst") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ArchimondeBossSummonedDoomfireTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArchimondeBossSummonedDoomfireTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "archimonde boss summoned doomfire") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class ArchimondeBotStoodInDoomfireTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
ArchimondeBotStoodInDoomfireTrigger(
|
||||
PlayerbotAI* botAI) : Trigger(botAI, "archimonde bot stood in doomfire") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
268
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.cpp
Normal file
268
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidHyjalSummitHelpers.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "Timer.h"
|
||||
|
||||
namespace HyjalSummitHelpers
|
||||
{
|
||||
// General
|
||||
|
||||
bool GetGroundedStepPosition(
|
||||
Player* bot, float destinationX, float destinationY, float moveDist,
|
||||
float& stepX, float& stepY, float& stepZ)
|
||||
{
|
||||
const float distance = bot->GetExactDist2d(destinationX, destinationY);
|
||||
if (distance <= 0.0f)
|
||||
return false;
|
||||
|
||||
const float stepDistance = std::min(moveDist, distance);
|
||||
const float deltaX = destinationX - bot->GetPositionX();
|
||||
const float deltaY = destinationY - bot->GetPositionY();
|
||||
stepX = bot->GetPositionX() + (deltaX / distance) * stepDistance;
|
||||
stepY = bot->GetPositionY() + (deltaY / distance) * stepDistance;
|
||||
stepZ = bot->GetMapWaterOrGroundLevel(stepX, stepY, bot->GetPositionZ());
|
||||
if (stepZ <= INVALID_HEIGHT)
|
||||
stepZ = bot->GetPositionZ();
|
||||
|
||||
bot->GetMap()->CheckCollisionAndGetValidCoords(
|
||||
bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(),
|
||||
stepX, stepY, stepZ, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RangedGroups GetRangedGroups(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
RangedGroups result;
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return result;
|
||||
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !botAI->IsRanged(member))
|
||||
continue;
|
||||
|
||||
if (botAI->IsHeal(member))
|
||||
result.healers.push_back(member);
|
||||
else
|
||||
result.rangedDps.push_back(member);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<size_t, size_t> GetBotCircleIndexAndCount(PlayerbotAI* botAI, Player* bot,
|
||||
const RangedGroups& groups)
|
||||
{
|
||||
const std::vector<Player*>& vec = botAI->IsHeal(bot) ? groups.healers : groups.rangedDps;
|
||||
auto it = std::find(vec.begin(), vec.end(), bot);
|
||||
size_t index = (it != vec.end()) ? std::distance(vec.begin(), it) : 0;
|
||||
|
||||
return {index, vec.size()};
|
||||
}
|
||||
|
||||
// Rage Winterchill
|
||||
|
||||
const Position WINTERCHILL_TANK_POSITION = { 5031.061f, -1784.521f, 1321.626f };
|
||||
std::unordered_map<ObjectGuid, bool> hasReachedWinterchillPosition;
|
||||
std::unordered_map<uint32, DeathAndDecayData> deathAndDecayPosition;
|
||||
|
||||
DeathAndDecayData* GetActiveWinterchillDeathAndDecay(uint32 instanceId)
|
||||
{
|
||||
auto instanceIt = deathAndDecayPosition.find(instanceId);
|
||||
if (instanceIt == deathAndDecayPosition.end())
|
||||
return nullptr;
|
||||
|
||||
const uint32 now = getMSTime();
|
||||
const uint32 elapsed = getMSTimeDiff(instanceIt->second.spawnTime, now);
|
||||
if (elapsed >= DEATH_AND_DECAY_REACQUIRE_DELAY)
|
||||
{
|
||||
deathAndDecayPosition.erase(instanceIt);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (elapsed >= DEATH_AND_DECAY_DURATION)
|
||||
return nullptr;
|
||||
|
||||
return &instanceIt->second;
|
||||
}
|
||||
|
||||
bool IsInDeathAndDecay(Player* bot, float radius)
|
||||
{
|
||||
const uint32 instanceId = bot->GetMap()->GetInstanceId();
|
||||
Aura* aura = bot->GetAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DEATH_AND_DECAY));
|
||||
if (aura)
|
||||
{
|
||||
DynamicObject* dynObj = aura->GetDynobjOwner();
|
||||
if (dynObj && dynObj->IsInWorld())
|
||||
{
|
||||
const uint32 now = getMSTime();
|
||||
auto instanceIt = deathAndDecayPosition.find(instanceId);
|
||||
if (instanceIt == deathAndDecayPosition.end() ||
|
||||
getMSTimeDiff(instanceIt->second.spawnTime, now) >= DEATH_AND_DECAY_REACQUIRE_DELAY)
|
||||
{
|
||||
deathAndDecayPosition[instanceId] =
|
||||
DeathAndDecayData{ dynObj->GetPosition(), now };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeathAndDecayData* data = GetActiveWinterchillDeathAndDecay(instanceId);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
return bot->GetExactDist2d(data->position) < radius;
|
||||
}
|
||||
|
||||
// Anetheron
|
||||
|
||||
const Position ANETHERON_TANK_POSITION = { 5033.177f, -1765.996f, 1324.195f };
|
||||
const Position ANETHERON_E_INFERNAL_POSITION = { 5016.578f, -1800.233f, 1323.070f };
|
||||
const Position ANETHERON_W_INFERNAL_POSITION = { 5048.911f, -1722.164f, 1321.408f };
|
||||
std::unordered_map<ObjectGuid, bool> hasReachedAnetheronPosition;
|
||||
|
||||
Player* GetInfernoTarget(Unit* anetheron)
|
||||
{
|
||||
if (!anetheron)
|
||||
return nullptr;
|
||||
|
||||
Spell* spell = anetheron->GetCurrentSpell(CURRENT_GENERIC_SPELL);
|
||||
if (spell && spell->m_spellInfo->Id ==
|
||||
static_cast<uint32>(HyjalSummitSpells::SPELL_INFERNO))
|
||||
{
|
||||
Unit* spellTarget = spell->m_targets.GetUnitTarget();
|
||||
if (spellTarget && spellTarget->IsPlayer())
|
||||
return spellTarget->ToPlayer();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Position& GetClosestInfernalTankPosition(Player* bot)
|
||||
{
|
||||
const Position& east = ANETHERON_E_INFERNAL_POSITION;
|
||||
const Position& west = ANETHERON_W_INFERNAL_POSITION;
|
||||
return (bot->GetExactDist2d(east.GetPositionX(), east.GetPositionY()) <=
|
||||
bot->GetExactDist2d(west.GetPositionX(), west.GetPositionY())) ? east : west;
|
||||
}
|
||||
|
||||
// Kaz'rogal
|
||||
|
||||
const Position KAZROGAL_TANK_TRANSITION_POSITION = { 5528.792f, -2636.486f, 1481.293f };
|
||||
const Position KAZROGAL_TANK_FINAL_POSITION = { 5511.514f, -2662.466f, 1480.288f };
|
||||
std::unordered_map<ObjectGuid, TankPositionState> kazrogalTankStep;
|
||||
std::unordered_map<ObjectGuid, bool> isBelowManaThreshold;
|
||||
|
||||
TankPositionState GetKazrogalTankPositionState(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||
if (!mainTank)
|
||||
return TankPositionState::Unknown;
|
||||
|
||||
auto it = kazrogalTankStep.find(mainTank->GetGUID());
|
||||
if (it != kazrogalTankStep.end())
|
||||
return it->second;
|
||||
|
||||
return TankPositionState::Unknown;
|
||||
}
|
||||
|
||||
// Azgalor
|
||||
|
||||
const Position AZGALOR_TANK_TRANSITION_POSITION = { 5486.787f, -2696.215f, 1482.007f };
|
||||
const Position AZGALOR_TANK_FINAL_POSITION = { 5496.379f, -2675.265f, 1481.053f };
|
||||
const Position AZGALOR_DOOMGUARD_POSITION = { 5485.555f, -2731.659f, 1485.555f };
|
||||
std::unordered_map<ObjectGuid, TankPositionState> azgalorTankStep;
|
||||
std::unordered_map<uint32, RainOfFireData> rainOfFirePosition;
|
||||
|
||||
RainOfFireData* GetActiveAzgalorRainOfFire(uint32 instanceId)
|
||||
{
|
||||
auto instanceIt = rainOfFirePosition.find(instanceId);
|
||||
if (instanceIt == rainOfFirePosition.end())
|
||||
return nullptr;
|
||||
|
||||
const uint32 now = getMSTime();
|
||||
const uint32 elapsed = getMSTimeDiff(instanceIt->second.spawnTime, now);
|
||||
if (elapsed >= RAIN_OF_FIRE_REACQUIRE_DELAY)
|
||||
{
|
||||
rainOfFirePosition.erase(instanceIt);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (elapsed >= RAIN_OF_FIRE_DURATION)
|
||||
return nullptr;
|
||||
|
||||
return &instanceIt->second;
|
||||
}
|
||||
|
||||
TankPositionState GetAzgalorTankPositionState(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||
if (!mainTank)
|
||||
return TankPositionState::Unknown;
|
||||
|
||||
auto it = azgalorTankStep.find(mainTank->GetGUID());
|
||||
if (it != azgalorTankStep.end())
|
||||
return it->second;
|
||||
|
||||
return TankPositionState::Unknown;
|
||||
}
|
||||
|
||||
bool IsInRainOfFire(Player* bot, float radius)
|
||||
{
|
||||
RainOfFireData* data = GetActiveAzgalorRainOfFire(bot->GetMap()->GetInstanceId());
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
return bot->GetExactDist2d(data->position) < radius;
|
||||
}
|
||||
|
||||
bool AnyGroupMemberHasDoom(Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member &&
|
||||
member->HasAura(static_cast<uint32>(HyjalSummitSpells::SPELL_DOOM)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Archimonde
|
||||
|
||||
const Position ARCHIMONDE_INITIAL_POSITION = { 5640.502f, -3421.238f, 1587.453f };
|
||||
std::unordered_map<uint32, AirBurstData> archimondeAirBurstTargets;
|
||||
std::unordered_map<uint32, std::vector<DoomfireTrailData>> doomfireTrails;
|
||||
std::unordered_map<ObjectGuid, uint32> doomfireLastSampleTime;
|
||||
|
||||
AirBurstData* GetRecentArchimondeAirBurst(uint32 instanceId)
|
||||
{
|
||||
auto instanceIt = archimondeAirBurstTargets.find(instanceId);
|
||||
if (instanceIt == archimondeAirBurstTargets.end())
|
||||
return nullptr;
|
||||
|
||||
constexpr uint32 airBurstReactionWindow = 2000;
|
||||
const uint32 now = getMSTime();
|
||||
if (getMSTimeDiff(instanceIt->second.castTime, now) >= airBurstReactionWindow)
|
||||
{
|
||||
archimondeAirBurstTargets.erase(instanceIt);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &instanceIt->second;
|
||||
}
|
||||
}
|
||||
143
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.h
Normal file
143
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitHelpers.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#ifndef _PLAYERBOT_RAIDHYJALSUMMITHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDHYJALSUMMITHELPERS_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Position.h"
|
||||
#include "Unit.h"
|
||||
|
||||
namespace HyjalSummitHelpers
|
||||
{
|
||||
enum class HyjalSummitSpells : uint32
|
||||
{
|
||||
// Rage Winterchill
|
||||
SPELL_DEATH_AND_DECAY = 31258,
|
||||
|
||||
// Anetheron
|
||||
SPELL_INFERNO = 31299,
|
||||
|
||||
// Kaz'rogal
|
||||
SPELL_MARK_OF_KAZROGAL = 31447,
|
||||
|
||||
// Azgalor
|
||||
SPELL_RAIN_OF_FIRE = 31340,
|
||||
SPELL_DOOM = 31347,
|
||||
|
||||
// Archimonde
|
||||
SPELL_DOOMFIRE = 31944, // Damaging part of trail
|
||||
SPELL_DOOMFIRE_DOT = 31969, // DoT after exiting trail
|
||||
SPELL_ARCHIMONDE_FEAR = 31970,
|
||||
SPELL_AIR_BURST = 32014,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Priest
|
||||
SPELL_FEAR_WARD = 6346,
|
||||
};
|
||||
|
||||
enum class HyjalSummitNpcs : uint32
|
||||
{
|
||||
// Archimonde
|
||||
NPC_DOOMFIRE = 18095,
|
||||
};
|
||||
|
||||
enum class TankPositionState : uint8
|
||||
{
|
||||
MovingToTransition = 0,
|
||||
MovingToFinal = 1,
|
||||
Positioned = 2,
|
||||
Unknown = 255,
|
||||
};
|
||||
|
||||
// General
|
||||
constexpr uint32 HYJAL_SUMMIT_MAP_ID = 534;
|
||||
struct RangedGroups
|
||||
{
|
||||
std::vector<Player*> healers;
|
||||
std::vector<Player*> rangedDps;
|
||||
};
|
||||
bool GetGroundedStepPosition(
|
||||
Player* bot, float destinationX, float destinationY, float moveDist,
|
||||
float& stepX, float& stepY, float& stepZ);
|
||||
RangedGroups GetRangedGroups(PlayerbotAI* botAI, Player* bot);
|
||||
std::pair<size_t, size_t> GetBotCircleIndexAndCount(PlayerbotAI* botAI, Player* bot,
|
||||
const RangedGroups& groups);
|
||||
|
||||
// Rage Winterchill
|
||||
extern const Position WINTERCHILL_TANK_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, bool> hasReachedWinterchillPosition;
|
||||
constexpr uint32 DEATH_AND_DECAY_DURATION = 15000;
|
||||
constexpr uint32 DEATH_AND_DECAY_REACQUIRE_DELAY = 20000;
|
||||
constexpr float DEATH_AND_DECAY_SAFE_RADIUS = 22.0f; // 20y radius + 1.5y player hitbox + 0.5y buffer
|
||||
struct DeathAndDecayData
|
||||
{
|
||||
Position position;
|
||||
uint32 spawnTime;
|
||||
};
|
||||
extern std::unordered_map<uint32, DeathAndDecayData> deathAndDecayPosition;
|
||||
DeathAndDecayData* GetActiveWinterchillDeathAndDecay(uint32 instanceId);
|
||||
bool IsInDeathAndDecay(Player* bot, float radius);
|
||||
|
||||
// Anetheron
|
||||
extern const Position ANETHERON_TANK_POSITION;
|
||||
extern const Position ANETHERON_E_INFERNAL_POSITION;
|
||||
extern const Position ANETHERON_W_INFERNAL_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, bool> hasReachedAnetheronPosition;
|
||||
Player* GetInfernoTarget(Unit* anetheron);
|
||||
const Position& GetClosestInfernalTankPosition(Player* bot);
|
||||
|
||||
// Kaz'rogal
|
||||
extern const Position KAZROGAL_TANK_TRANSITION_POSITION;
|
||||
extern const Position KAZROGAL_TANK_FINAL_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, TankPositionState> kazrogalTankStep;
|
||||
extern std::unordered_map<ObjectGuid, bool> isBelowManaThreshold;
|
||||
TankPositionState GetKazrogalTankPositionState(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
// Azgalor
|
||||
extern const Position AZGALOR_TANK_TRANSITION_POSITION;
|
||||
extern const Position AZGALOR_TANK_FINAL_POSITION;
|
||||
extern const Position AZGALOR_DOOMGUARD_POSITION;
|
||||
extern std::unordered_map<ObjectGuid, TankPositionState> azgalorTankStep;
|
||||
constexpr uint32 RAIN_OF_FIRE_DURATION = 10000;
|
||||
constexpr uint32 RAIN_OF_FIRE_REACQUIRE_DELAY = 15000;
|
||||
constexpr float RAIN_OF_FIRE_RADIUS = 17.0f; // 15y radius + 1.5y player hitbox + 0.5y buffer
|
||||
struct RainOfFireData
|
||||
{
|
||||
Position position;
|
||||
uint32 spawnTime;
|
||||
};
|
||||
extern std::unordered_map<uint32, RainOfFireData> rainOfFirePosition;
|
||||
TankPositionState GetAzgalorTankPositionState(PlayerbotAI* botAI, Player* bot);
|
||||
RainOfFireData* GetActiveAzgalorRainOfFire(uint32 instanceId);
|
||||
bool IsInRainOfFire(Player* bot, float radius);
|
||||
bool AnyGroupMemberHasDoom(Player* bot);
|
||||
|
||||
// Archimonde
|
||||
constexpr float AIR_BURST_SAFE_DISTANCE = 15.0f;
|
||||
struct AirBurstData
|
||||
{
|
||||
ObjectGuid targetGuid;
|
||||
uint32 castTime;
|
||||
};
|
||||
struct DoomfireTrailData
|
||||
{
|
||||
Position position;
|
||||
uint32 recordTime;
|
||||
};
|
||||
extern const Position ARCHIMONDE_INITIAL_POSITION;
|
||||
extern std::unordered_map<uint32, AirBurstData> archimondeAirBurstTargets;
|
||||
extern std::unordered_map<uint32, std::vector<DoomfireTrailData>> doomfireTrails;
|
||||
extern std::unordered_map<ObjectGuid, uint32> doomfireLastSampleTime;
|
||||
AirBurstData* GetRecentArchimondeAirBurst(uint32 instanceId);
|
||||
}
|
||||
|
||||
#endif
|
||||
211
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitScripts.cpp
Normal file
211
src/Ai/Raid/HyjalSummit/Util/RaidHyjalSummitScripts.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||
*/
|
||||
|
||||
#include "RaidHyjalSummitHelpers.h"
|
||||
#include "AllCreatureScript.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Player.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "DynamicObjectScript.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Spell.h"
|
||||
#include "Timer.h"
|
||||
|
||||
using namespace HyjalSummitHelpers;
|
||||
|
||||
static Player* GetFirstPlayerSpellTarget(Spell* spell, Unit* caster)
|
||||
{
|
||||
if (!spell || !caster)
|
||||
return nullptr;
|
||||
|
||||
if (Unit* unitTarget = spell->m_targets.GetUnitTarget())
|
||||
return unitTarget->ToPlayer();
|
||||
|
||||
std::list<TargetInfo> const& targets = *spell->GetUniqueTargetInfo();
|
||||
for (TargetInfo const& targetInfo : targets)
|
||||
{
|
||||
if (Player* target = ObjectAccessor::GetPlayer(*caster, targetInfo.targetGUID))
|
||||
return target;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool ShouldInterruptForArchimondeAirBurst(PlayerbotAI* botAI, Player* bot, Player* target)
|
||||
{
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
Player* mainTank = GetGroupMainTank(botAI, bot);
|
||||
if (!mainTank || bot == mainTank)
|
||||
return false;
|
||||
|
||||
float distanceToMainTank = bot->GetExactDist2d(mainTank);
|
||||
|
||||
return (target == mainTank || target == bot) &&
|
||||
distanceToMainTank < AIR_BURST_SAFE_DISTANCE;
|
||||
}
|
||||
|
||||
// Records the active Rain of Fire dynamic object so that melee bots can avoid it by running
|
||||
// away from Azgalor or swapping to a Doomguard; the standard FleePosition() logic to avoid aoe
|
||||
// can take melee in front of Azgalor, resulting in them getting cleaved
|
||||
class AzgalorRainOfFireScript : public DynamicObjectScript
|
||||
{
|
||||
public:
|
||||
AzgalorRainOfFireScript() : DynamicObjectScript("AzgalorRainOfFireScript") {}
|
||||
|
||||
void OnUpdate(DynamicObject* dynobj, uint32 /*diff*/) override
|
||||
{
|
||||
if (dynobj->GetSpellId() != static_cast<uint32>(HyjalSummitSpells::SPELL_RAIN_OF_FIRE))
|
||||
return;
|
||||
|
||||
uint32 instanceId = dynobj->GetMap()->GetInstanceId();
|
||||
if (GetActiveAzgalorRainOfFire(instanceId))
|
||||
return;
|
||||
|
||||
uint32 now = getMSTime();
|
||||
auto instanceIt = rainOfFirePosition.find(instanceId);
|
||||
if (instanceIt != rainOfFirePosition.end() &&
|
||||
getMSTimeDiff(instanceIt->second.spawnTime, now) < RAIN_OF_FIRE_REACQUIRE_DELAY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldTrackRainOfFire = false;
|
||||
Map::PlayerList const& players = dynobj->GetMap()->GetPlayers();
|
||||
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
|
||||
{
|
||||
Player* player = it->GetSource();
|
||||
if (!player || !player->IsAlive())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (!botAI || !botAI->HasStrategy("hyjal", BOT_STATE_COMBAT))
|
||||
continue;
|
||||
|
||||
shouldTrackRainOfFire = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!shouldTrackRainOfFire)
|
||||
return;
|
||||
|
||||
rainOfFirePosition[instanceId] = RainOfFireData{ dynobj->GetPosition(), now };
|
||||
}
|
||||
};
|
||||
|
||||
// Records the position of each Doomfire NPC at regular intervals so that bots can avoid
|
||||
// the persistent fire trail it leaves behind. Each sample is tagged with a timestamp and
|
||||
// expires after TRAIL_DURATION ms, matching the lifetime of a Doomfire DynamicObject (18s)
|
||||
class ArchimondeDoomfireTrailScript : public AllCreatureScript
|
||||
{
|
||||
public:
|
||||
ArchimondeDoomfireTrailScript() : AllCreatureScript("ArchimondeDoomfireTrailScript") {}
|
||||
|
||||
void OnAllCreatureUpdate(Creature* creature, uint32 /*diff*/) override
|
||||
{
|
||||
if (creature->GetEntry() != static_cast<uint32>(HyjalSummitNpcs::NPC_DOOMFIRE))
|
||||
return;
|
||||
|
||||
uint32 now = getMSTime();
|
||||
ObjectGuid guid = creature->GetGUID();
|
||||
|
||||
auto& lastSample = doomfireLastSampleTime[guid];
|
||||
if (getMSTimeDiff(lastSample, now) < 500)
|
||||
return;
|
||||
|
||||
lastSample = now;
|
||||
|
||||
uint32 instanceId = creature->GetMap()->GetInstanceId();
|
||||
auto& trail = doomfireTrails[instanceId];
|
||||
|
||||
DoomfireTrailData data;
|
||||
data.position = creature->GetPosition();
|
||||
data.recordTime = now;
|
||||
trail.push_back(data);
|
||||
|
||||
constexpr uint32 TRAIL_DURATION = 18000;
|
||||
trail.erase(std::remove_if(trail.begin(), trail.end(),
|
||||
[now](const DoomfireTrailData& d)
|
||||
{
|
||||
return getMSTimeDiff(d.recordTime, now) > TRAIL_DURATION;
|
||||
}), trail.end());
|
||||
|
||||
constexpr float DOOMFIRE_DANGER_RANGE = 10.0f;
|
||||
Map::PlayerList const& players = creature->GetMap()->GetPlayers();
|
||||
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
|
||||
{
|
||||
Player* player = it->GetSource();
|
||||
if (!player || !player->IsAlive())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (!botAI || !botAI->HasStrategy("hyjal", BOT_STATE_COMBAT) ||
|
||||
creature->GetDistance(player) > DOOMFIRE_DANGER_RANGE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
botAI->RequestSpellInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
void OnCreatureRemoveWorld(Creature* creature) override
|
||||
{
|
||||
if (creature->GetEntry() != static_cast<uint32>(HyjalSummitNpcs::NPC_DOOMFIRE))
|
||||
return;
|
||||
|
||||
doomfireLastSampleTime.erase(creature->GetGUID());
|
||||
}
|
||||
};
|
||||
|
||||
class ArchimondeAirBurstSpellListenerScript : public AllSpellScript
|
||||
{
|
||||
public:
|
||||
ArchimondeAirBurstSpellListenerScript() :
|
||||
AllSpellScript("ArchimondeAirBurstSpellListenerScript") {}
|
||||
|
||||
void OnSpellCast(
|
||||
Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool /*skipCheck*/) override
|
||||
{
|
||||
if (!spell || !caster || !spellInfo)
|
||||
return;
|
||||
|
||||
if (spellInfo->Id != static_cast<uint32>(HyjalSummitSpells::SPELL_AIR_BURST))
|
||||
return;
|
||||
|
||||
Player* target = GetFirstPlayerSpellTarget(spell, caster);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
archimondeAirBurstTargets[caster->GetMap()->GetInstanceId()] =
|
||||
AirBurstData{ target->GetGUID(), getMSTime() };
|
||||
|
||||
Map::PlayerList const& players = caster->GetMap()->GetPlayers();
|
||||
for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
|
||||
{
|
||||
Player* player = it->GetSource();
|
||||
if (!player || !player->IsAlive())
|
||||
continue;
|
||||
|
||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||
if (!botAI || !botAI->HasStrategy("hyjal", BOT_STATE_COMBAT) ||
|
||||
!ShouldInterruptForArchimondeAirBurst(botAI, player, target))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
botAI->RequestSpellInterrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_HyjalSummitBotScripts()
|
||||
{
|
||||
new AzgalorRainOfFireScript();
|
||||
new ArchimondeDoomfireTrailScript();
|
||||
new ArchimondeAirBurstSpellListenerScript();
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidIccTriggers.h"
|
||||
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
#ifndef _PLAYERBOT_RAIDICCSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDICCSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
#include "RaidIccMultipliers.h"
|
||||
|
||||
class RaidIccStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDKARAZHANTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidKarazhanTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidKarazhanTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#define _PLAYERBOT_RAIDKARAZHANSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidKarazhanStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidMagtheridonTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidMagtheridonTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#define _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidMagtheridonStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcTriggers.h"
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDMCSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDMCSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidMcStrategy : public Strategy
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidNaxxTriggers.h"
|
||||
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDNAXXSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidNaxxStrategy : public Strategy
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOsTriggers.h"
|
||||
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDOSSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDOSSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidOsStrategy : public Strategy
|
||||
|
||||
@ -99,8 +99,12 @@ bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event /*event*/)
|
||||
if (bot->IsWithinDist2d(bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->radius))
|
||||
return false; // Already safe
|
||||
|
||||
// Stop current spell first
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
|
||||
// bot->Yell("Moving to Safe Zone!", LANG_UNIVERSAL);
|
||||
return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bot->GetPositionZ(),
|
||||
return MoveTo(bot->GetMapId(), bestZone->pos.GetPositionX(), bestZone->pos.GetPositionY(), bestZone->pos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDONYXIAACTIONS_H_
|
||||
#define _PLAYERBOT_RAIDONYXIAACTIONS_H_
|
||||
|
||||
#include "Action.h"
|
||||
#include "AttackAction.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "MovementActions.h"
|
||||
@ -45,42 +44,45 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
|
||||
private:
|
||||
std::vector<SafeZone> GetSafeZonesForBreath(uint32 spellId)
|
||||
static std::vector<SafeZone> GetSafeZonesForBreath(uint32 spellId)
|
||||
{
|
||||
// Define your safe zone coordinates based on the map
|
||||
// Example assumes Onyxia's lair map coordinates
|
||||
float z = bot->GetPositionZ(); // Stay at current height
|
||||
// Safe zone coordinates based on the map
|
||||
// Assumes Onyxia's lair map coordinates
|
||||
|
||||
switch (spellId)
|
||||
{
|
||||
case 17086: // N to S
|
||||
case 18351: // S to N
|
||||
return {SafeZone{Position(-10.0f, -180.0f, z), 5.0f},
|
||||
SafeZone{Position(-20.0f, -250.0f, z), 5.0f}}; // Bottom Safe Zone
|
||||
return {
|
||||
SafeZone{Position(-10.0f, -180.0f, -87.0f), 5.0f},
|
||||
SafeZone{Position(-20.0f, -250.0f, -88.0f), 5.0f}
|
||||
}; // Bottom Safe Zone
|
||||
|
||||
case 18576: // E to W
|
||||
case 18609: // W to E
|
||||
return {
|
||||
SafeZone{Position(20.0f, -210.0f, z), 5.0f},
|
||||
SafeZone{Position(-75.0f, -210.0f, z), 5.0f},
|
||||
SafeZone{Position(20.0f, -210.0f, -85.5f), 5.0f},
|
||||
SafeZone{Position(-75.0f, -210.0f, -83.4f), 5.0f},
|
||||
}; // Left Safe Zone
|
||||
|
||||
case 18564: // SE to NW
|
||||
case 18584: // NW to SE
|
||||
return {
|
||||
SafeZone{Position(-60.0f, -195.0f, z), 5.0f},
|
||||
SafeZone{Position(10.0f, -240.0f, z), 5.0f},
|
||||
SafeZone{Position(-60.0f, -195.0f, -85.0f), 5.0f},
|
||||
SafeZone{Position(10.0f, -240.0f, -85.9f), 5.0f},
|
||||
}; // NW Safe Zone
|
||||
|
||||
case 18596: // SW to NE
|
||||
case 18617: // NE to SW
|
||||
return {
|
||||
SafeZone{Position(7.0f, -185.0f, z), 5.0f},
|
||||
SafeZone{Position(-60.0f, -240.0f, z), 5.0f},
|
||||
SafeZone{Position(7.0f, -185.0f, -86.2f), 5.0f},
|
||||
SafeZone{Position(-60.0f, -240.0f, -85.2f), 5.0f},
|
||||
}; // NE Safe Zone
|
||||
|
||||
default:
|
||||
return {SafeZone{Position(0.0f, 0.0f, z), 5.0f}}; // Fallback center - shouldn't ever happen
|
||||
return {
|
||||
SafeZone{Position(-40.0f, -214.0f, -86.6f), 5.0f}
|
||||
}; // Fallback center - shouldn't ever happen
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDONYXIATRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDONYXIATRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidOnyxiaTriggers.h"
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "RaidNaxxStrategy.h"
|
||||
#include "RaidSSCStrategy.h"
|
||||
#include "RaidTempestKeepStrategy.h"
|
||||
#include "RaidHyjalSummitStrategy.h"
|
||||
#include "RaidZulAmanStrategy.h"
|
||||
#include "RaidOsStrategy.h"
|
||||
#include "RaidEoEStrategy.h"
|
||||
@ -33,6 +34,7 @@ public:
|
||||
creators["naxx"] = &RaidStrategyContext::naxx;
|
||||
creators["ssc"] = &RaidStrategyContext::ssc;
|
||||
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
|
||||
creators["hyjal"] = &RaidStrategyContext::hyjal;
|
||||
creators["zulaman"] = &RaidStrategyContext::zulaman;
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||
@ -52,6 +54,7 @@ private:
|
||||
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
|
||||
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
|
||||
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
|
||||
static Strategy* hyjal(PlayerbotAI* botAI) { return new RaidHyjalSummitStrategy(botAI); }
|
||||
static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(botAI); }
|
||||
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
|
||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
#define _PLAYERBOT_RAIDSSCTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidSSCTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidSSCTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
#define _PLAYERBOT_RAIDSSCSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidSSCStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidTempestKeepTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidTempestKeepTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#define _PLAYERBOT_RAIDTEMPESTKEEPSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidTempestKeepStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDULDUARTRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDULDUARTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidUlduarTriggers.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
||||
#define _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidUlduarStrategy : public Strategy
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
#ifndef _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
||||
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidVoATriggers.h"
|
||||
|
||||
@ -3,10 +3,6 @@
|
||||
#define _PLAYERBOT_RAIDVOASTRATEGY_H
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "string"
|
||||
#include "Trigger.h"
|
||||
#include "vector"
|
||||
|
||||
class RaidVoAStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
#define _PLAYERBOT_RAIDZULAMANTRIGGERCONTEXT_H
|
||||
|
||||
#include "RaidZulAmanTriggers.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "NamedObjectContext.h"
|
||||
|
||||
class RaidZulAmanTriggerContext : public NamedObjectContext<Trigger>
|
||||
{
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
#define _PLAYERBOT_RAIDZULAMANSTRATEGY_H_
|
||||
|
||||
#include "Strategy.h"
|
||||
#include "Multiplier.h"
|
||||
|
||||
class RaidZulAmanStrategy : public Strategy
|
||||
{
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
|
||||
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitActionContext.h"
|
||||
#include "Ai/Raid/ZulAman/RaidZulAmanActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
|
||||
@ -34,6 +35,7 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
|
||||
actionContexts.Add(new RaidMagtheridonActionContext());
|
||||
actionContexts.Add(new RaidSSCActionContext());
|
||||
actionContexts.Add(new RaidTempestKeepActionContext());
|
||||
actionContexts.Add(new RaidHyjalSummitActionContext());
|
||||
actionContexts.Add(new RaidZulAmanActionContext());
|
||||
actionContexts.Add(new RaidNaxxActionContext());
|
||||
actionContexts.Add(new RaidOsActionContext());
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
|
||||
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
|
||||
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
|
||||
#include "Ai/Raid/HyjalSummit/RaidHyjalSummitTriggerContext.h"
|
||||
#include "Ai/Raid/ZulAman/RaidZulAmanTriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
|
||||
@ -35,6 +36,7 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
|
||||
triggerContexts.Add(new RaidNaxxTriggerContext());
|
||||
triggerContexts.Add(new RaidSSCTriggerContext());
|
||||
triggerContexts.Add(new RaidTempestKeepTriggerContext());
|
||||
triggerContexts.Add(new RaidHyjalSummitTriggerContext());
|
||||
triggerContexts.Add(new RaidZulAmanTriggerContext());
|
||||
triggerContexts.Add(new RaidOsTriggerContext());
|
||||
triggerContexts.Add(new RaidEoETriggerContext());
|
||||
|
||||
@ -33,8 +33,11 @@ bool ExternalEventHelper::ParseChatCommand(std::string const command, Player* ow
|
||||
if (!ChatHelper::parseableItem(command))
|
||||
return false;
|
||||
|
||||
if (sPlayerbotAIConfig.enableAutoTradeOnItemMention)
|
||||
{
|
||||
HandleCommand("c", command, owner);
|
||||
HandleCommand("t", command, owner);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -619,7 +619,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
|
||||
else
|
||||
password = accountName;
|
||||
|
||||
AccountMgr::CreateAccount(accountName, password);
|
||||
sAccountMgr->CreateAccount(accountName, password);
|
||||
|
||||
LOG_DEBUG("playerbots", "Account {} created for random bots", accountName.c_str());
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "ChannelMgr.h"
|
||||
#include "CharacterPackets.h"
|
||||
#include "ChatHelper.h"
|
||||
#include "CheckMountStateAction.h"
|
||||
#include "Common.h"
|
||||
#include "CreatureData.h"
|
||||
#include "EmoteAction.h"
|
||||
@ -54,9 +55,9 @@
|
||||
#include "Unit.h"
|
||||
#include "UpdateTime.h"
|
||||
#include "Vehicle.h"
|
||||
#include "../../../../src/server/scripts/Spells/spell_dk.cpp"
|
||||
|
||||
const int SPELL_TITAN_GRIP = 49152;
|
||||
constexpr uint32 SPELL_TITAN_GRIP = 49152;
|
||||
constexpr uint32 SPELL_DK_FROST_PRESENCE = 48263;
|
||||
|
||||
std::vector<std::string> PlayerbotAI::dispel_whitelist = {
|
||||
"mutating injection",
|
||||
@ -1380,6 +1381,17 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
|
||||
// */
|
||||
return;
|
||||
}
|
||||
case SMSG_DISMOUNT:
|
||||
{
|
||||
WorldPacket p(packet);
|
||||
p.rpos(0);
|
||||
ObjectGuid guid;
|
||||
p >> guid.ReadAsPacked();
|
||||
if (guid != bot->GetGUID())
|
||||
return;
|
||||
CheckMountStateAction::CompleteDismount(bot);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
botOutgoingPacketHandlers.AddPacket(packet);
|
||||
}
|
||||
@ -1588,6 +1600,27 @@ void PlayerbotAI::ClearStrategies(BotState type)
|
||||
e->removeAllStrategies();
|
||||
}
|
||||
|
||||
// Resets only the combat or non-combat engine: wipe strategies, repopulate with class/spec defaults,
|
||||
// re-apply current map's instance strategy (if any), and call Init() to rebuild trigger/action lists.
|
||||
void PlayerbotAI::SelectiveResetStrategies(BotState type)
|
||||
{
|
||||
Engine* e = engines[type];
|
||||
if (!e)
|
||||
return;
|
||||
|
||||
e->removeAllStrategies();
|
||||
|
||||
if (type == BOT_STATE_COMBAT)
|
||||
AiFactory::AddDefaultCombatStrategies(bot, this, e);
|
||||
else if (type == BOT_STATE_NON_COMBAT)
|
||||
AiFactory::AddDefaultNonCombatStrategies(bot, this, e);
|
||||
|
||||
if (sPlayerbotAIConfig.applyInstanceStrategies)
|
||||
ApplyInstanceStrategies(bot->GetMapId());
|
||||
|
||||
e->Init();
|
||||
}
|
||||
|
||||
std::vector<std::string> PlayerbotAI::GetStrategies(BotState type)
|
||||
{
|
||||
Engine* e = engines[type];
|
||||
@ -1601,11 +1634,12 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
{
|
||||
static const std::vector<std::string> allInstanceStrategies =
|
||||
{
|
||||
"aq20", "bwl", "karazhan", "gruulslair", "icc", "magtheridon", "moltencore",
|
||||
"naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar", "voa", "wotlk-an", "wotlk-cos",
|
||||
"wotlk-dtk", "wotlk-eoe", "wotlk-fos", "wotlk-gd", "wotlk-hol", "wotlk-hor",
|
||||
"wotlk-hos", "wotlk-nex", "wotlk-occ", "wotlk-ok", "wotlk-os", "wotlk-pos",
|
||||
"wotlk-toc", "wotlk-uk", "wotlk-up", "wotlk-vh", "zulaman"
|
||||
"aq20", "bwl", "karazhan", "gruulslair", "hyjal", "icc", "magtheridon",
|
||||
"moltencore", "naxx", "onyxia", "ssc", "tbc-ac", "tempestkeep", "ulduar",
|
||||
"voa", "wotlk-an", "wotlk-cos", "wotlk-dtk", "wotlk-eoe", "wotlk-fos",
|
||||
"wotlk-gd", "wotlk-hol", "wotlk-hor", "wotlk-hos", "wotlk-nex", "wotlk-occ",
|
||||
"wotlk-ok", "wotlk-os", "wotlk-pos", "wotlk-toc", "wotlk-uk", "wotlk-up",
|
||||
"wotlk-vh", "zulaman"
|
||||
};
|
||||
|
||||
for (const std::string& strat : allInstanceStrategies)
|
||||
@ -1635,6 +1669,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
case 533:
|
||||
strategyName = "naxx"; // Naxxramas
|
||||
break;
|
||||
case 534:
|
||||
strategyName = "hyjal"; // The Battle for Mount Hyjal (Hyjal Summit)
|
||||
break;
|
||||
case 544:
|
||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||
break;
|
||||
|
||||
@ -405,6 +405,7 @@ public:
|
||||
std::string const qualifier = "");
|
||||
void ChangeStrategy(std::string const name, BotState type);
|
||||
void ClearStrategies(BotState type);
|
||||
void SelectiveResetStrategies(BotState type);
|
||||
std::vector<std::string> GetStrategies(BotState type);
|
||||
Strategy* GetStrategy(std::string const name, BotState type);
|
||||
void ApplyInstanceStrategies(uint32 mapId, bool tellMaster = false);
|
||||
|
||||
@ -1542,6 +1542,24 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
|
||||
// if master is logging out, log out all bots
|
||||
case CMSG_LOGOUT_REQUEST:
|
||||
{
|
||||
Player* master = GetMaster();
|
||||
if (master)
|
||||
{
|
||||
// Replicate the AFK logout prevention checks from WorldSession::HandleLogoutRequestOpcode
|
||||
// so bots are not logged out when the master's own logout is going to be prevented.
|
||||
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(master->GetAreaId());
|
||||
bool preventAfkSanctuaryLogout = sWorld->getIntConfig(CONFIG_AFK_PREVENT_LOGOUT) == 1
|
||||
&& master->isAFK() && areaEntry && areaEntry->IsSanctuary();
|
||||
|
||||
bool preventAfkLogout = sWorld->getIntConfig(CONFIG_AFK_PREVENT_LOGOUT) == 2
|
||||
&& master->isAFK();
|
||||
|
||||
if (preventAfkSanctuaryLogout || preventAfkLogout)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LogoutAllBots();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2070,7 +2070,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
|
||||
|
||||
bot->DurabilityRepairAll(false, 1.0f, false);
|
||||
bot->SetFullHealth();
|
||||
bot->SetPvP(true);
|
||||
bot->SetPvP(sWorld->IsPvPRealm());
|
||||
PlayerbotFactory factory(bot, bot->GetLevel());
|
||||
factory.Refresh();
|
||||
|
||||
@ -2635,6 +2635,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
|
||||
{
|
||||
// ObjectGuid::LowType guid = player->GetGUID().GetCounter(); //not used, conditional could be rewritten for
|
||||
// simplicity. line marked for removal.
|
||||
player->SetPvP(sWorld->IsPvPRealm());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -730,7 +730,6 @@ std::vector<WorldPosition> WorldPosition::getPathStepFrom(WorldPosition startPos
|
||||
// the previous step's endpoint, giving the 40-attempt walker
|
||||
// its intended multi-tile reach.
|
||||
PathGenerator path(pathUnit);
|
||||
path.AddExcludeFlag(NAV_GROUND_STEEP);
|
||||
path.CalculatePath(startPos.GetPositionX(), startPos.GetPositionY(), startPos.GetPositionZ(),
|
||||
GetPositionX(), GetPositionY(), GetPositionZ(), false);
|
||||
|
||||
|
||||
@ -518,6 +518,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
|
||||
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.AllowedLogFiles", ""),
|
||||
allowedLogFiles);
|
||||
enableAutoTradeOnItemMention = sConfigMgr->GetOption<bool>("AiPlayerbot.EnableAutoTradeOnItemMention", true);
|
||||
LoadListString<std::vector<std::string>>(sConfigMgr->GetOption<std::string>("AiPlayerbot.TradeActionExcludedPrefixes", ""),
|
||||
tradeActionExcludedPrefixes);
|
||||
|
||||
|
||||
@ -306,6 +306,7 @@ public:
|
||||
uint32 iterationsPerTick;
|
||||
|
||||
std::mutex m_logMtx;
|
||||
bool enableAutoTradeOnItemMention;
|
||||
std::vector<std::string> tradeActionExcludedPrefixes;
|
||||
std::vector<std::string> allowedLogFiles;
|
||||
std::unordered_map<std::string, std::pair<FILE*, bool>> logFiles;
|
||||
|
||||
@ -526,6 +526,7 @@ public:
|
||||
void AddPlayerbotsSecureLoginScripts();
|
||||
|
||||
void AddSC_TempestKeepBotScripts();
|
||||
void AddSC_HyjalSummitBotScripts();
|
||||
|
||||
void AddPlayerbotsScripts()
|
||||
{
|
||||
@ -541,4 +542,5 @@ void AddPlayerbotsScripts()
|
||||
AddPlayerbotsCommandscripts();
|
||||
PlayerBotsGuildValidationScript();
|
||||
AddSC_TempestKeepBotScripts();
|
||||
AddSC_HyjalSummitBotScripts();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user