Compare commits

..

No commits in common. "ca9f23a8e39fff4fbc80dec31577ac5d08916a0a" and "d32b10de155283d376f033899d4a23b0a92853ea" have entirely different histories.

46 changed files with 26 additions and 3573 deletions

View File

@ -2,7 +2,6 @@ name: Enforce test-staging → master
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened, edited]
branches: branches:
- master - master
- test-staging - test-staging

View File

@ -566,10 +566,7 @@ AiPlayerbot.AutoGearScoreLimit = 0
# Default: food, taxi, and raid are enabled # Default: food, taxi, and raid are enabled
AiPlayerbot.BotCheats = "food,taxi,raid" AiPlayerbot.BotCheats = "food,taxi,raid"
# List of attunement quests (comma-separated list of quest IDs) that are automatically completed for all bots. # Attunement quests (comma-separated list of quest IDs)
# While mod-playerbots does not restore removed attunement requirements, although other mods, such as mod-individual-progression, may do so.
# This is meant to exclude bots from such requirements.
#
# Default: # Default:
# Caverns of Time - Part 1 # Caverns of Time - Part 1
# - 10279, To The Master's Lair # - 10279, To The Master's Lair
@ -595,17 +592,7 @@ AiPlayerbot.BotCheats = "food,taxi,raid"
# #
# Serpentshrine Cavern # Serpentshrine Cavern
# - 10901, The Cudgel of Kar'desh # - 10901, The Cudgel of Kar'desh
# AiPlayerbot.AttunementQuests = 10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901
# The Eye
# - 10888, Trial of the Naaru: Magtheridon
#
# Mount Hyjal
# - 10445, The Vials of Eternity
#
# Black Temple
# - 10985, A Distraction for Akama
#
AiPlayerbot.AttunementQuests = 10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985
# #
# #
@ -809,10 +796,6 @@ AiPlayerbot.LimitGearExpansion = 1
# Set between 0 (0%) and 1 (100%) # Set between 0 (0%) and 1 (100%)
AiPlayerbot.RandomGearLoweringChance = 0 AiPlayerbot.RandomGearLoweringChance = 0
# Unobtainable or unusable items (comma-separated list of item IDs)
# Default: Chilton Wand (12468), Totem of the Earthen Ring (46978)
AiPlayerbot.UnobtainableItems = 12468,46978
# Randombots check player's gearscore level and deny the group invitation if it's too low # Randombots check player's gearscore level and deny the group invitation if it's too low
# Default: 0 (disabled) # Default: 0 (disabled)
AiPlayerbot.GearScoreCheck = 0 AiPlayerbot.GearScoreCheck = 0
@ -2230,4 +2213,4 @@ AiPlayerbot.SummonAtInnkeepersEnabled = 1
# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots. # 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots.
# Buffs will be applied on PP, Sindragosa and Lich King # Buffs will be applied on PP, Sindragosa and Lich King
AiPlayerbot.EnableICCBuffs = 1 AiPlayerbot.EnableICCBuffs = 1

View File

@ -237,20 +237,6 @@ bool MaxDpsChatShortcutAction::Execute(Event /*event*/)
return true; return true;
} }
bool NaxxChatShortcutAction::Execute(Event /*event*/)
{
Player* master = GetMaster();
if (!master)
return false;
botAI->Reset();
botAI->ChangeStrategy("+naxx", BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+naxx", BOT_STATE_COMBAT);
botAI->TellMasterNoFacing("Add Naxx Strategies!");
// bot->Say("Add Naxx Strategies!", LANG_UNIVERSAL);
return true;
}
bool BwlChatShortcutAction::Execute(Event /*event*/) bool BwlChatShortcutAction::Execute(Event /*event*/)
{ {
Player* master = GetMaster(); Player* master = GetMaster();

View File

@ -85,13 +85,6 @@ public:
bool Execute(Event event) override; bool Execute(Event event) override;
}; };
class NaxxChatShortcutAction : public Action
{
public:
NaxxChatShortcutAction(PlayerbotAI* ai) : Action(ai, "naxx chat shortcut") {}
virtual bool Execute(Event event);
};
class BwlChatShortcutAction : public Action class BwlChatShortcutAction : public Action
{ {
public: public:

View File

@ -854,11 +854,6 @@ float MovementAction::GetFollowAngle()
if (!group) if (!group)
return 0.0f; return 0.0f;
// Prevent bots with orphaned raid groups from dividing by 0, which freezes the server.
uint32 memberCount = group->GetMembersCount();
if (memberCount <= 1)
return 0.0f;
uint32 index = 1; uint32 index = 1;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
@ -866,7 +861,7 @@ float MovementAction::GetFollowAngle()
continue; continue;
if (ref->GetSource() == bot) if (ref->GetSource() == bot)
return 2 * M_PI / (memberCount - 1) * index; return 2 * M_PI / (group->GetMembersCount() - 1) * index;
++index; ++index;
} }

View File

@ -187,7 +187,6 @@ public:
creators["guild leave"] = &ChatActionContext::guild_leave; creators["guild leave"] = &ChatActionContext::guild_leave;
creators["rtsc"] = &ChatActionContext::rtsc; creators["rtsc"] = &ChatActionContext::rtsc;
creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut; creators["bwl chat shortcut"] = &ChatActionContext::bwl_chat_shortcut;
creators["naxx chat shortcut"] = &ChatActionContext::naxx_chat_shortcut;
creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps; creators["tell estimated dps"] = &ChatActionContext::tell_estimated_dps;
creators["join"] = &ChatActionContext::join; creators["join"] = &ChatActionContext::join;
creators["lfg"] = &ChatActionContext::lfg; creators["lfg"] = &ChatActionContext::lfg;
@ -299,7 +298,6 @@ private:
static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); } static Action* guild_remove(PlayerbotAI* botAI) { return new GuildRemoveAction(botAI); }
static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); } static Action* guild_leave(PlayerbotAI* botAI) { return new GuildLeaveAction(botAI); }
static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); } static Action* rtsc(PlayerbotAI* botAI) { return new RTSCAction(botAI); }
static Action* naxx_chat_shortcut(PlayerbotAI* ai) { return new NaxxChatShortcutAction(ai); }
static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); } static Action* bwl_chat_shortcut(PlayerbotAI* ai) { return new BwlChatShortcutAction(ai); }
static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); } static Action* tell_estimated_dps(PlayerbotAI* ai) { return new TellEstimatedDpsAction(ai); }
static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); } static Action* join(PlayerbotAI* ai) { return new JoinGroupAction(ai); }

View File

@ -127,6 +127,7 @@ public:
creators["guild leave"] = &ChatTriggerContext::guild_leave; creators["guild leave"] = &ChatTriggerContext::guild_leave;
creators["rtsc"] = &ChatTriggerContext::rtsc; creators["rtsc"] = &ChatTriggerContext::rtsc;
creators["drink"] = &ChatTriggerContext::drink; creators["drink"] = &ChatTriggerContext::drink;
// creators["bwl"] = &ChatTriggerContext::bwl;
creators["dps"] = &ChatTriggerContext::dps; creators["dps"] = &ChatTriggerContext::dps;
creators["disperse"] = &ChatTriggerContext::disperse; creators["disperse"] = &ChatTriggerContext::disperse;
creators["calc"] = &ChatTriggerContext::calc; creators["calc"] = &ChatTriggerContext::calc;
@ -244,6 +245,7 @@ private:
static Trigger* guild_leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "guild leave"); } static Trigger* guild_leave(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "guild leave"); }
static Trigger* rtsc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rtsc"); } static Trigger* rtsc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "rtsc"); }
static Trigger* drink(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "drink"); } static Trigger* drink(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "drink"); }
// static Trigger* bwl(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "bwl"); }
static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); } static Trigger* dps(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "dps"); }
static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); } static Trigger* disperse(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "disperse"); }
static Trigger* calc(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "calc"); } static Trigger* calc(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "calc"); }

View File

@ -83,8 +83,6 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
new TriggerNode("target", { NextAction("tell target", relevance) })); new TriggerNode("target", { NextAction("tell target", relevance) }));
triggers.push_back( triggers.push_back(
new TriggerNode("ready", { NextAction("ready check", relevance) })); new TriggerNode("ready", { NextAction("ready check", relevance) }));
triggers.push_back(
new TriggerNode("naxx", {NextAction("naxx chat shortcut", relevance)}));
triggers.push_back( triggers.push_back(
new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) })); new TriggerNode("bwl", { NextAction("bwl chat shortcut", relevance) }));
triggers.push_back( triggers.push_back(

View File

@ -116,9 +116,9 @@ bool AttumenTheHuntsmanStackBehindAction::Execute(Event /*event*/)
float rearX = attumenMounted->GetPositionX() + std::cos(orientation) * distanceBehind; float rearX = attumenMounted->GetPositionX() + std::cos(orientation) * distanceBehind;
float rearY = attumenMounted->GetPositionY() + std::sin(orientation) * distanceBehind; float rearY = attumenMounted->GetPositionY() + std::sin(orientation) * distanceBehind;
if (bot->GetDistance2d(rearX, rearY) > 1.0f) if (bot->GetExactDist2d(rearX, rearY) > 1.0f)
{ {
return MoveTo(KARAZHAN_MAP_ID, rearX, rearY, bot->GetPositionZ(), false, false, false, false, return MoveTo(KARAZHAN_MAP_ID, rearX, rearY, attumenMounted->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_FORCED, true, false); MovementPriority::MOVEMENT_FORCED, true, false);
} }
@ -1178,7 +1178,7 @@ bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event /*event*/)
bot->AttackStop(); bot->AttackStop();
bot->InterruptNonMeleeSpells(true); bot->InterruptNonMeleeSpells(true);
return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false, return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false); MovementPriority::MOVEMENT_FORCED, true, false);
} }
} }
@ -1244,7 +1244,7 @@ bool PrinceMalchezaarMainTankMovementAction::Execute(Event /*event*/)
{ {
bot->AttackStop(); bot->AttackStop();
return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false, return MoveTo(KARAZHAN_MAP_ID, bestDestX, bestDestY, bestDestZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, true); MovementPriority::MOVEMENT_FORCED, true, false);
} }
} }

View File

@ -80,19 +80,6 @@ float AttumenTheHuntsmanWaitForDpsMultiplier::GetValue(Action* action)
return 1.0f; return 1.0f;
} }
// Disables co +disperse and co +tank face
float MaidenOfVirtueDisableCombatFormationMoveMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "maiden of virtue"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}
// The assist tank should stay on the boss to be 2nd on aggro and tank Hateful Bolts // The assist tank should stay on the boss to be 2nd on aggro and tank Hateful Bolts
float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action) float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
{ {
@ -106,19 +93,6 @@ float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
return 1.0f; return 1.0f;
} }
// Disables co +disperse and co +tank face
float TheCuratorDisableCombatFormationMoveMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "the curator"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}
// Save Bloodlust/Heroism for Evocation (100% increased damage) // Save Bloodlust/Heroism for Evocation (100% increased damage)
float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
{ {
@ -376,11 +350,17 @@ float NightbaneDisableMovementMultiplier::GetValue(Action* action)
if (dynamic_cast<CastBlinkBackAction*>(action) || if (dynamic_cast<CastBlinkBackAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action) || dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<FleeAction*>(action) || dynamic_cast<FleeAction*>(action))
(dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action)))
{
return 0.0f; return 0.0f;
// Disable CombatFormationMoveAction for all bots except:
// (1) main tank and (2) only during the ground phase, other melee
if (botAI->IsRanged(bot) ||
(botAI->IsMelee(bot) && !botAI->IsMainTank(bot) &&
nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z))
{
if (dynamic_cast<CombatFormationMoveAction*>(action))
return 0.0f;
} }
return 1.0f; return 1.0f;

View File

@ -27,14 +27,6 @@ public:
virtual float GetValue(Action* action); virtual float GetValue(Action* action);
}; };
class MaidenOfVirtueDisableCombatFormationMoveMultiplier : public Multiplier
{
public:
MaidenOfVirtueDisableCombatFormationMoveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "maiden of virtue disable combat formation move multiplier") {}
virtual float GetValue(Action* action);
};
class TheCuratorDisableTankAssistMultiplier : public Multiplier class TheCuratorDisableTankAssistMultiplier : public Multiplier
{ {
public: public:
@ -43,14 +35,6 @@ public:
virtual float GetValue(Action* action); virtual float GetValue(Action* action);
}; };
class TheCuratorDisableCombatFormationMoveMultiplier : public Multiplier
{
public:
TheCuratorDisableCombatFormationMoveMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "the curator disable combat formation move multiplier") {}
virtual float GetValue(Action* action);
};
class TheCuratorDelayBloodlustAndHeroismMultiplier : public Multiplier class TheCuratorDelayBloodlustAndHeroismMultiplier : public Multiplier
{ {
public: public:

View File

@ -146,9 +146,7 @@ void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers
multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI)); multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI));
multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI)); multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI));
multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI)); multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI));
multipliers.push_back(new MaidenOfVirtueDisableCombatFormationMoveMultiplier(botAI));
multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI)); multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI));
multipliers.push_back(new TheCuratorDisableCombatFormationMoveMultiplier(botAI));
multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI)); multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI));
multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI)); multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI));
multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI)); multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI));

View File

@ -62,6 +62,7 @@ namespace KarazhanHelpers
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
// Terestian Illhoof // Terestian Illhoof
NPC_TERESTIAN_ILLHOOF = 15688,
NPC_DEMON_CHAINS = 17248, NPC_DEMON_CHAINS = 17248,
NPC_KILREK = 17229, NPC_KILREK = 17229,

View File

@ -1,326 +0,0 @@
#ifndef _PLAYERBOT_RAIDNAXXACTIONS_H
#define _PLAYERBOT_RAIDNAXXACTIONS_H
#include "Action.h"
#include "AttackAction.h"
#include "GenericActions.h"
#include "MovementActions.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "RaidNaxxBossHelper.h"
class GrobbulusGoBehindAction : public MovementAction
{
public:
GrobbulusGoBehindAction(PlayerbotAI* ai, float distance = 24.0f, float delta_angle = M_PI / 8)
: MovementAction(ai, "grobbulus go behind")
{
this->distance = distance;
this->delta_angle = delta_angle;
}
virtual bool Execute(Event event);
protected:
float distance, delta_angle;
};
class GrobbulusRotateAction : public RotateAroundTheCenterPointAction
{
public:
GrobbulusRotateAction(PlayerbotAI* botAI)
: RotateAroundTheCenterPointAction(botAI, "rotate grobbulus", 3281.23f, -3310.38f, 35.0f, 8, true, M_PI) {}
virtual bool isUseful() override
{
return RotateAroundTheCenterPointAction::isUseful() && botAI->IsMainTank(bot) &&
AI_VALUE2(bool, "has aggro", "boss target");
}
uint32 GetCurrWaypoint() override;
};
class GrobblulusMoveCenterAction : public MoveInsideAction
{
public:
GrobblulusMoveCenterAction(PlayerbotAI* ai) : MoveInsideAction(ai, 3281.23f, -3310.38f, 5.0f) {}
};
class GrobbulusMoveAwayAction : public MovementAction
{
public:
GrobbulusMoveAwayAction(PlayerbotAI* ai, float distance = 18.0f)
: MovementAction(ai, "grobbulus move away"), distance(distance)
{
}
bool Execute(Event event) override;
private:
float distance;
};
//class HeiganDanceAction : public MovementAction
//{
//public:
// HeiganDanceAction(PlayerbotAI* ai) : MovementAction(ai, "heigan dance")
// {
// this->last_eruption_ms = 0;
// this->platform_phase = false;
// ResetSafe();
// waypoints.push_back(std::make_pair(2794.88f, -3668.12f));
// waypoints.push_back(std::make_pair(2775.49f, -3674.43f));
// waypoints.push_back(std::make_pair(2762.30f, -3684.59f));
// waypoints.push_back(std::make_pair(2755.99f, -3703.96f));
// platform = std::make_pair(2794.26f, -3706.67f);
// }
//
//protected:
// bool CalculateSafe();
// void ResetSafe()
// {
// curr_safe = 0;
// curr_dir = 1;
// }
// void NextSafe()
// {
// curr_safe += curr_dir;
// if (curr_safe == 3 || curr_safe == 0)
// {
// curr_dir = -curr_dir;
// }
// }
// uint32 last_eruption_ms;
// bool platform_phase;
// uint32 curr_safe, curr_dir;
// std::vector<std::pair<float, float>> waypoints;
// std::pair<float, float> platform;
//};
//
//class HeiganDanceMeleeAction : public HeiganDanceAction
//{
//public:
// HeiganDanceMeleeAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {}
// virtual bool Execute(Event event);
//};
//
//class HeiganDanceRangedAction : public HeiganDanceAction
//{
//public:
// HeiganDanceRangedAction(PlayerbotAI* ai) : HeiganDanceAction(ai) {}
// virtual bool Execute(Event event);
//};
class ThaddiusAttackNearestPetAction : public AttackAction
{
public:
ThaddiusAttackNearestPetAction(PlayerbotAI* ai) : AttackAction(ai, "thaddius attack nearest pet"), helper(ai) {}
virtual bool Execute(Event event);
virtual bool isUseful();
private:
ThaddiusBossHelper helper;
};
// class ThaddiusMeleeToPlaceAction : public MovementAction
// {
// public:
// ThaddiusMeleeToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius melee to place") {}
// virtual bool Execute(Event event);
// virtual bool isUseful();
// };
// class ThaddiusRangedToPlaceAction : public MovementAction
// {
// public:
// ThaddiusRangedToPlaceAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius ranged to place") {}
// virtual bool Execute(Event event);
// virtual bool isUseful();
// };
class ThaddiusMoveToPlatformAction : public MovementAction
{
public:
ThaddiusMoveToPlatformAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move to platform") {}
virtual bool Execute(Event event);
virtual bool isUseful();
};
class ThaddiusMovePolarityAction : public MovementAction
{
public:
ThaddiusMovePolarityAction(PlayerbotAI* ai) : MovementAction(ai, "thaddius move polarity") {}
virtual bool Execute(Event event);
virtual bool isUseful();
};
class RazuviousUseObedienceCrystalAction : public MovementAction
{
public:
RazuviousUseObedienceCrystalAction(PlayerbotAI* ai)
: MovementAction(ai, "razuvious use obedience crystal"), helper(ai)
{
}
bool Execute(Event event) override;
private:
RazuviousBossHelper helper;
};
class RazuviousTargetAction : public AttackAction
{
public:
RazuviousTargetAction(PlayerbotAI* ai) : AttackAction(ai, "razuvious target"), helper(ai) {}
bool Execute(Event event) override;
private:
RazuviousBossHelper helper;
};
class HorsemanAttractAlternativelyAction : public AttackAction
{
public:
HorsemanAttractAlternativelyAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attract alternatively"), helper(ai)
{
}
bool Execute(Event event) override;
protected:
FourhorsemanBossHelper helper;
};
class HorsemanAttactInOrderAction : public AttackAction
{
public:
HorsemanAttactInOrderAction(PlayerbotAI* ai) : AttackAction(ai, "horseman attact in order"), helper(ai) {}
bool Execute(Event event) override;
protected:
FourhorsemanBossHelper helper;
};
// class SapphironGroundMainTankPositionAction : public MovementAction
// {
// public:
// SapphironGroundMainTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground main tank
// position") {} virtual bool Execute(Event event);
// };
class SapphironGroundPositionAction : public MovementAction
{
public:
SapphironGroundPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron ground position"), helper(ai) {}
bool Execute(Event event) override;
protected:
SapphironBossHelper helper;
};
class SapphironFlightPositionAction : public MovementAction
{
public:
SapphironFlightPositionAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron flight position"), helper(ai) {}
bool Execute(Event event) override;
protected:
SapphironBossHelper helper;
bool MoveToNearestIcebolt();
};
// class SapphironAvoidChillAction : public MovementAction
// {
// public:
// SapphironAvoidChillAction(PlayerbotAI* ai) : MovementAction(ai, "sapphiron avoid chill") {}
// virtual bool Execute(Event event);
// };
class KelthuzadChooseTargetAction : public AttackAction
{
public:
KelthuzadChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "kel'thuzad choose target"), helper(ai) {}
virtual bool Execute(Event event);
private:
KelthuzadBossHelper helper;
};
class KelthuzadPositionAction : public MovementAction
{
public:
KelthuzadPositionAction(PlayerbotAI* ai) : MovementAction(ai, "kel'thuzad position"), helper(ai) {}
virtual bool Execute(Event event);
private:
KelthuzadBossHelper helper;
};
class AnubrekhanChooseTargetAction : public AttackAction
{
public:
AnubrekhanChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "anub'rekhan choose target") {}
bool Execute(Event event) override;
};
class AnubrekhanPositionAction : public RotateAroundTheCenterPointAction
{
public:
AnubrekhanPositionAction(PlayerbotAI* ai)
: RotateAroundTheCenterPointAction(ai, "anub'rekhan position", 3272.49f, -3476.27f, 45.0f, 16) {}
bool Execute(Event event) override;
};
class GluthChooseTargetAction : public AttackAction
{
public:
GluthChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "gluth choose target"), helper(ai) {}
bool Execute(Event event) override;
private:
GluthBossHelper helper;
};
class GluthPositionAction : public RotateAroundTheCenterPointAction
{
public:
GluthPositionAction(PlayerbotAI* ai)
: RotateAroundTheCenterPointAction(ai, "gluth position", 3293.61f, -3149.01f, 12.0f, 12), helper(ai) {}
bool Execute(Event event) override;
private:
GluthBossHelper helper;
};
class GluthSlowdownAction : public Action
{
public:
GluthSlowdownAction(PlayerbotAI* ai) : Action(ai, "gluth slowdown"), helper(ai) {}
bool Execute(Event event) override;
private:
GluthBossHelper helper;
};
class LoathebPositionAction : public MovementAction
{
public:
LoathebPositionAction(PlayerbotAI* ai) : MovementAction(ai, "loatheb position"), helper(ai) {}
virtual bool Execute(Event event);
private:
LoathebBossHelper helper;
};
class LoathebChooseTargetAction : public AttackAction
{
public:
LoathebChooseTargetAction(PlayerbotAI* ai) : AttackAction(ai, "loatheb choose target"), helper(ai) {}
virtual bool Execute(Event event);
private:
LoathebBossHelper helper;
};
//class PatchwerkRangedPositionAction : public MovementAction
//{
//public:
// PatchwerkRangedPositionAction(PlayerbotAI* ai) : MovementAction(ai, "patchwerk ranged position") {}
// bool Execute(Event event) override;
//};
#endif

View File

@ -1,81 +0,0 @@
#include "RaidNaxxActions.h"
#include "ObjectGuid.h"
#include "Playerbots.h"
bool AnubrekhanChooseTargetAction::Execute(Event /*event*/)
{
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
Unit* target_boss = nullptr;
std::vector<Unit*> target_guards;
for (ObjectGuid const guid : attackers)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "crypt guard"))
target_guards.push_back(unit);
if (botAI->EqualLowercaseName(unit->GetName(), "anub'rekhan"))
target_boss = unit;
}
if (botAI->IsMainTank(bot))
target = target_boss;
else
{
if (target_guards.size() == 0)
target = target_boss;
else
{
if (botAI->IsAssistTank(bot))
{
for (Unit* t : target_guards)
{
if (target == nullptr || (target->GetVictim() && target->GetVictim()->ToPlayer() &&
botAI->IsTank(target->GetVictim()->ToPlayer())))
target = t;
}
}
else
{
for (Unit* t : target_guards)
{
if (target == nullptr || target->GetHealthPct() > t->GetHealthPct())
target = t;
}
}
}
}
if (context->GetValue<Unit*>("current target")->Get() == target)
return false;
return Attack(target);
}
bool AnubrekhanPositionAction::Execute(Event /*event*/)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
if (!boss)
return false;
bool inPhase = botAI->HasAura("locust swarm", boss) || boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (inPhase)
{
if (botAI->IsMainTank(bot))
{
uint32 nearest = FindNearestWaypoint();
uint32 next_point;
if (inPhase)
next_point = (nearest + 1) % intervals;
else
next_point = nearest;
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, false,
false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
return MoveInside(533, 3272.49f, -3476.27f, bot->GetPositionZ(), 3.0f, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}

View File

@ -1,3 +0,0 @@
#include "RaidNaxxActions.h"
// Reserved for Faerlina-specific actions.

View File

@ -1,59 +0,0 @@
#include "RaidNaxxActions.h"
#include "Playerbots.h"
bool HorsemanAttractAlternativelyAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
helper.CalculatePosToGo(bot);
auto [posX, posY] = helper.CurrentAttractPos();
if (MoveTo(bot->GetMapId(), posX, posY, helper.posZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
return true;
Unit* attackTarget = helper.CurrentAttackTarget();
if (context->GetValue<Unit*>("current target")->Get() != attackTarget)
return Attack(attackTarget);
return false;
}
bool HorsemanAttactInOrderAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
Unit* target = nullptr;
Unit* thane = AI_VALUE2(Unit*, "find target", "thane korth'azz");
Unit* lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
Unit* sir = AI_VALUE2(Unit*, "find target", "sir zeliek");
Unit* fourth = AI_VALUE2(Unit*, "find target", "baron rivendare");
if (!fourth)
fourth = AI_VALUE2(Unit*, "find target", "highlord mograine");
std::vector<Unit*> attack_order;
if (botAI->IsAssistTank(bot))
attack_order = {fourth, thane, lady, sir};
else
attack_order = {thane, fourth, lady, sir};
for (Unit* t : attack_order)
{
if (t && t->IsAlive())
{
target = t;
break;
}
}
if (target)
{
if (context->GetValue<Unit*>("current target")->Get() == target && botAI->GetState() == BOT_STATE_COMBAT)
return false;
if (!bot->IsWithinLOSInMap(target))
return MoveNear(target, 22.0f, MovementPriority::MOVEMENT_COMBAT);
return Attack(target);
}
return false;
}

View File

@ -1,178 +0,0 @@
#include "RaidNaxxActions.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "SharedDefines.h"
bool GluthChooseTargetAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
GuidVector attackers = context->GetValue<GuidVector>("possible targets")->Get();
Unit* target = nullptr;
Unit* target_boss = nullptr;
std::vector<Unit*> target_zombies;
for (GuidVector::iterator i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (!unit->IsAlive())
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "zombie chow"))
target_zombies.push_back(unit);
if (botAI->EqualLowercaseName(unit->GetName(), "gluth"))
target_boss = unit;
}
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
target = target_boss;
else if (botAI->IsAssistTankOfIndex(bot, 1))
{
for (Unit* t : target_zombies)
{
if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() != bot && t->GetDistance2d(bot) <= 10.0f)
{
if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot))
target = t;
}
}
}
else if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0 || botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
{
// prevent zombie go straight to gluth
for (Unit* t : target_zombies)
{
if (t->GetHealthPct() > helper.decimatedZombiePct && t->GetVictim() == target_boss &&
t->GetDistance2d(bot) <= sPlayerbotAIConfig.spellDistance)
{
if (!target || t->GetDistance2d(bot) < target->GetDistance2d(bot))
target = t;
}
}
if (!target)
target = target_boss;
}
else
{
for (Unit* t : target_zombies)
{
if (t->GetHealthPct() <= helper.decimatedZombiePct)
{
if (target == nullptr ||
target->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second) >
t->GetDistance2d(helper.mainTankPos25.first, helper.mainTankPos25.second))
target = t;
}
}
if (target == nullptr)
target = target_boss;
}
if (!target || context->GetValue<Unit*>("current target")->Get() == target)
return false;
if (target_boss && target == target_boss)
return Attack(target, true);
return Attack(target, false);
// return Attack(target);
}
bool GluthPositionAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
{
if (AI_VALUE2(bool, "has aggro", "boss target"))
{
if (raid25)
{
if (MoveTo(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), false, false, false,
false, MovementPriority::MOVEMENT_COMBAT))
return true;
return MoveInside(NAXX_MAP_ID, helper.mainTankPos25.first, helper.mainTankPos25.second, bot->GetPositionZ(), 2.0f,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
if (MoveTo(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), false, false, false,
false, MovementPriority::MOVEMENT_COMBAT))
return true;
return MoveInside(NAXX_MAP_ID, helper.mainTankPos10.first, helper.mainTankPos10.second, bot->GetPositionZ(), 2.0f,
MovementPriority::MOVEMENT_COMBAT);
}
}
}
else if (botAI->IsAssistTankOfIndex(bot, 1))
{
if (helper.BeforeDecimate())
{
if (MoveTo(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), false, false,
false, false, MovementPriority::MOVEMENT_COMBAT))
return true;
return MoveInside(bot->GetMapId(), helper.beforeDecimatePos.first, helper.beforeDecimatePos.second, bot->GetPositionZ(), 2.0f,
MovementPriority::MOVEMENT_COMBAT);
}
else
{
if (AI_VALUE2(bool, "has aggro", "current target"))
{
uint32 nearest = FindNearestWaypoint();
uint32 next_point = (nearest + 1) % intervals;
return MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
}
else if (botAI->IsRangedDps(bot))
{
if (raid25)
{
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 0)
return MoveInside(NAXX_MAP_ID, helper.leftSlowDownPos.first, helper.leftSlowDownPos.second, bot->GetPositionZ(), 0.0f,
MovementPriority::MOVEMENT_COMBAT);
if (botAI->GetClassIndex(bot, CLASS_HUNTER) == 1)
return MoveInside(NAXX_MAP_ID, helper.rightSlowDownPos.first, helper.rightSlowDownPos.second, bot->GetPositionZ(), 0.0f,
MovementPriority::MOVEMENT_COMBAT);
}
return MoveInside(NAXX_MAP_ID, helper.rangedPos.first, helper.rangedPos.second, bot->GetPositionZ(), 3.0f,
MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsHeal(bot))
return MoveInside(NAXX_MAP_ID, helper.healPos.first, helper.healPos.second, bot->GetPositionZ(), 0.0f,
MovementPriority::MOVEMENT_COMBAT);
return false;
}
bool GluthSlowdownAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
if (!raid25)
return false;
if (helper.JustStartCombat())
return false;
switch (bot->getClass())
{
case CLASS_HUNTER:
return botAI->CastSpell("frost trap", bot);
break;
default:
break;
}
return false;
}

View File

@ -1,3 +0,0 @@
#include "RaidNaxxActions.h"
// Reserved for Gothik-specific actions.

View File

@ -1,46 +0,0 @@
#include "RaidNaxxActions.h"
#include "Playerbots.h"
bool GrobbulusGoBehindAction::Execute(Event /*event*/)
{
Unit* boss = AI_VALUE(Unit*, "boss target");
if (!boss)
return false;
// Position* pos = boss->GetPosition();
float orientation = boss->GetOrientation() + M_PI + delta_angle;
float x = boss->GetPositionX();
float y = boss->GetPositionY();
float z = boss->GetPositionZ();
float rx = x + cos(orientation) * distance;
float ry = y + sin(orientation) * distance;
return MoveTo(bot->GetMapId(), rx, ry, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
bool GrobbulusMoveAwayAction::Execute(Event /*event*/)
{
Unit* boss = AI_VALUE(Unit*, "boss target");
if (!boss)
return false;
const float currentDistance = bot->GetExactDist2d(boss);
if (currentDistance >= distance)
return false;
const float angle = boss->GetAngle(bot);
const float x = boss->GetPositionX() + cos(angle) * distance;
const float y = boss->GetPositionY() + sin(angle) * distance;
const float z = bot->GetPositionZ();
return MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
uint32 GrobbulusRotateAction::GetCurrWaypoint()
{
uint32 current = FindNearestWaypoint();
if (clockwise)
return (current + 1) % intervals;
return (current + intervals - 1) % intervals;
}

View File

@ -1,77 +0,0 @@
#include "Playerbots.h"
#include "RaidNaxxActions.h"
#include "RaidNaxxSpellIds.h"
#include "Spell.h"
#include "Timer.h"
//bool HeiganDanceAction::CalculateSafe()
//{
// Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
// if (!boss)
// {
// return false;
// }
// uint32 now = getMSTime();
// platform_phase = boss->IsWithinDist2d(platform.first, platform.second, 10.0f);
// if (last_eruption_ms != 0 && now - last_eruption_ms > 15000)
// {
// ResetSafe();
// }
// if (boss->HasUnitState(UNIT_STATE_CASTING))
// {
// Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
// if (!spell)
// {
// spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
// }
// if (spell)
// {
// SpellInfo const* info = spell->GetSpellInfo();
// bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10});
// if (!isEruption && info && info->SpellName[LOCALE_enUS])
// {
// // Fallback to name for custom spell data.
// isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption");
// }
// if (isEruption)
// {
// if (last_eruption_ms == 0 || now - last_eruption_ms > 500)
// {
// NextSafe();
// }
// last_eruption_ms = now;
// }
// }
// }
// return true;
//}
//
//bool HeiganDanceMeleeAction::Execute(Event event)
//{
// CalculateSafe();
// if (!platform_phase && botAI->IsMainTank(bot) && !AI_VALUE2(bool, "has aggro", "boss target"))
// {
// return false;
// }
// assert(curr_safe >= 0 && curr_safe <= 3);
// return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(),
// botAI->IsMainTank(bot) ? 0 : 0, MovementPriority::MOVEMENT_COMBAT);
//}
//
//bool HeiganDanceRangedAction::Execute(Event event)
//{
// CalculateSafe();
// if (!platform_phase)
// {
// if (MoveTo(bot->GetMapId(), platform.first, platform.second, 276.54f, false, false, false, false,
// MovementPriority::MOVEMENT_COMBAT))
// {
// return true;
// }
// return MoveInside(bot->GetMapId(), platform.first, platform.second, 276.54f, 2.0f,
// MovementPriority::MOVEMENT_COMBAT);
// }
// botAI->InterruptSpell();
// return MoveInside(bot->GetMapId(), waypoints[curr_safe].first, waypoints[curr_safe].second, bot->GetPositionZ(), 0,
// MovementPriority::MOVEMENT_COMBAT);
//}

View File

@ -1,177 +0,0 @@
#include "RaidNaxxActions.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
bool KelthuzadChooseTargetAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
Unit *target_soldier = nullptr, *target_weaver = nullptr, *target_abomination = nullptr, *target_kelthuzad = nullptr,
*target_guardian = nullptr;
for (auto i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "guardian of icecrown"))
{
if (!target_guardian)
target_guardian = unit;
else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() &&
target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) &&
botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()))
{
target_guardian = unit;
}
else if (unit->GetVictim() && target_guardian->GetVictim() && unit->GetVictim()->ToPlayer() &&
target_guardian->GetVictim()->ToPlayer() && !botAI->IsAssistTank(unit->GetVictim()->ToPlayer()) &&
!botAI->IsAssistTank(target_guardian->GetVictim()->ToPlayer()) &&
target_guardian->GetDistance2d(helper.center.first, helper.center.second) >
bot->GetDistance2d(unit))
{
target_guardian = unit;
}
}
if (unit->GetDistance2d(helper.center.first, helper.center.second) > 30.0f)
continue;
if (bot->GetDistance2d(unit) > sPlayerbotAIConfig.spellDistance)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "unstoppable abomination"))
{
if (target_abomination == nullptr ||
target_abomination->GetDistance2d(helper.center.first, helper.center.second) >
unit->GetDistance2d(helper.center.first, helper.center.second))
{
target_abomination = unit;
}
}
if (botAI->EqualLowercaseName(unit->GetName(), "soldier of the frozen wastes"))
{
if (target_soldier == nullptr ||
target_soldier->GetDistance2d(helper.center.first, helper.center.second) >
unit->GetDistance2d(helper.center.first, helper.center.second))
{
target_soldier = unit;
}
}
if (botAI->EqualLowercaseName(unit->GetName(), "soul weaver"))
{
if (target_weaver == nullptr || target_weaver->GetDistance2d(helper.center.first, helper.center.second) >
unit->GetDistance2d(helper.center.first, helper.center.second))
target_weaver = unit;
}
if (botAI->EqualLowercaseName(unit->GetName(), "kel'thuzad"))
target_kelthuzad = unit;
}
std::vector<Unit*> targets;
if (botAI->IsRanged(bot))
{
if (botAI->GetRangedDpsIndex(bot) <= 1)
targets = {target_soldier, target_weaver, target_abomination, target_kelthuzad};
else
targets = {target_weaver, target_soldier, target_abomination, target_kelthuzad};
}
else if (botAI->IsAssistTank(bot))
targets = {target_abomination, target_guardian, target_kelthuzad};
else
targets = {target_abomination, target_kelthuzad};
for (Unit* t : targets)
{
if (t)
{
target = t;
break;
}
}
if (context->GetValue<Unit*>("current target")->Get() == target)
return false;
if (target_kelthuzad && target == target_kelthuzad)
return Attack(target, true);
return Attack(target, false);
}
bool KelthuzadPositionAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
if (helper.IsPhaseOne())
{
if (AI_VALUE(Unit*, "current target") == nullptr)
return MoveInside(NAXX_MAP_ID, helper.center.first, helper.center.second, bot->GetPositionZ(), 3.0f,
MovementPriority::MOVEMENT_COMBAT);
}
else if (helper.IsPhaseTwo())
{
Unit* shadow_fissure = helper.GetAnyShadowFissure();
if (!shadow_fissure || !bot->IsWithinDistInMap(shadow_fissure, 10.0f))
{
float distance, angle;
if (botAI->IsMainTank(bot))
{
if (AI_VALUE2(bool, "has aggro", "current target"))
return MoveTo(NAXX_MAP_ID, helper.tank_pos.first, helper.tank_pos.second, bot->GetPositionZ(), false, false, false,
false, MovementPriority::MOVEMENT_COMBAT);
else
return false;
}
else if (botAI->IsRanged(bot))
{
uint32 index = botAI->GetRangedIndex(bot);
if (index < 8)
{
distance = 20.0f;
angle = index * M_PI / 4;
}
else
{
distance = 32.0f;
angle = (index - 8) * M_PI / 4;
}
float dx, dy;
dx = helper.center.first + cos(angle) * distance;
dy = helper.center.second + sin(angle) * distance;
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsTank(bot))
{
Unit* cur_tar = AI_VALUE(Unit*, "current target");
if (cur_tar && cur_tar->GetVictim() && cur_tar->GetVictim()->ToPlayer() &&
botAI->EqualLowercaseName(cur_tar->GetName(), "guardian of icecrown") &&
botAI->IsAssistTank(cur_tar->GetVictim()->ToPlayer()))
{
return MoveTo(NAXX_MAP_ID, helper.assist_tank_pos.first, helper.assist_tank_pos.second, bot->GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
else
return false;
}
}
else
{
float dx, dy;
float angle;
if (!botAI->IsRanged(bot))
angle = shadow_fissure->GetAngle(helper.center.first, helper.center.second);
else
angle = bot->GetAngle(shadow_fissure) + M_PI;
dx = shadow_fissure->GetPositionX() + cos(angle) * 10.0f;
dy = shadow_fissure->GetPositionY() + sin(angle) * 10.0f;
return MoveTo(NAXX_MAP_ID, dx, dy, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}

View File

@ -1,55 +0,0 @@
#include "RaidNaxxActions.h"
#include "Playerbots.h"
bool LoathebPositionAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
if (botAI->IsTank(bot))
{
if (AI_VALUE2(bool, "has aggro", "boss target"))
return MoveTo(533, helper.mainTankPos.first, helper.mainTankPos.second, bot->GetPositionZ(), false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
else if (botAI->IsRanged(bot))
return MoveInside(533, helper.rangePos.first, helper.rangePos.second, bot->GetPositionZ(), 1.0f,
MovementPriority::MOVEMENT_COMBAT);
return false;
}
bool LoathebChooseTargetAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
Unit* target_boss = nullptr;
Unit* target_spore = nullptr;
for (auto i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (!unit->IsAlive())
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "spore"))
target_spore = unit;
if (botAI->EqualLowercaseName(unit->GetName(), "loatheb"))
target_boss = unit;
}
if (target_spore && bot->GetDistance2d(target_spore) <= 1.0f)
target = target_spore;
else
target = target_boss;
if (!target || context->GetValue<Unit*>("current target")->Get() == target)
return false;
return Attack(target);
}

View File

@ -1,3 +0,0 @@
#include "RaidNaxxActions.h"
// Reserved for Maexxna-specific actions.

View File

@ -1,3 +0,0 @@
#include "RaidNaxxActions.h"
// Reserved for Noth-specific actions.

View File

@ -1,31 +0,0 @@
#include "RaidNaxxActions.h"
#include <algorithm>
#include <cmath>
//bool PatchwerkRangedPositionAction::Execute(Event event)
//{
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
// if (!boss)
// return false;
//
// constexpr float minDistance = 12.0f;
// constexpr float maxDistance = 15.0f;
// const float distance = bot->GetExactDist2d(boss);
//
// if (distance >= minDistance && distance <= maxDistance)
// return false;
//
// const float desiredDistance = std::clamp(distance, minDistance, maxDistance);
// float angle = boss->GetAngle(bot);
//
// if (distance < 0.1f)
// angle = boss->GetOrientation();
//
// const float x = boss->GetPositionX() + std::cos(angle) * desiredDistance;
// const float y = boss->GetPositionY() + std::sin(angle) * desiredDistance;
// const float z = bot->GetPositionZ();
//
// return MoveTo(boss->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true,
// false);
//}

View File

@ -1,147 +0,0 @@
#include "RaidNaxxActions.h"
#include "ObjectGuid.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "SharedDefines.h"
bool RazuviousUseObedienceCrystalAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
// bot->GetCharm
if (Unit* charm = bot->GetCharm())
{
Unit* target = AI_VALUE2(Unit*, "find target", "instructor razuvious");
if (!target)
return false;
if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE)
{
charm->GetMotionMaster()->Clear();
charm->GetMotionMaster()->MoveChase(target);
charm->GetAI()->AttackStart(target);
}
Aura* forceObedience = botAI->GetAura("force obedience", charm);
uint32 duration_time;
if (!forceObedience)
{
forceObedience = botAI->GetAura("mind control", charm);
duration_time = 60000;
}
else
duration_time = 90000;
if (!forceObedience)
return false;
if (charm->GetDistance(target) <= 0.51f)
{
// taunt
bool tauntUseful = true;
if (forceObedience->GetDuration() <= (duration_time - 5000))
{
if (target->GetVictim() && botAI->HasAura(29061, target->GetVictim()))
tauntUseful = false;
if (forceObedience->GetDuration() <= 3000)
tauntUseful = false;
}
if (forceObedience->GetDuration() >= (duration_time - 500))
tauntUseful = false;
if (tauntUseful && !charm->HasSpellCooldown(29060))
{
// shield
if (!charm->HasSpellCooldown(29061))
{
charm->CastSpell(charm, 29061, true);
charm->AddSpellCooldown(29061, 0, 30 * 1000);
}
charm->CastSpell(target, 29060, true);
charm->AddSpellCooldown(29060, 0, 20 * 1000);
}
// strike
if (!charm->HasSpellCooldown(61696))
{
charm->CastSpell(target, 61696, true);
charm->AddSpellCooldown(61696, 0, 4 * 1000);
}
}
}
else
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
{
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (auto i = npcs.begin(); i != npcs.end(); i++)
{
Creature* unit = botAI->GetCreature(*i);
if (!unit)
continue;
if (botAI->IsMainTank(bot) && unit->GetSpawnId() != 128352)
continue;
if (!botAI->IsMainTank(bot) && unit->GetSpawnId() != 128353)
continue;
if (MoveTo(unit, 0.0f, MovementPriority::MOVEMENT_COMBAT))
return true;
Creature* creature = bot->GetNPCIfCanInteractWith(*i, UNIT_NPC_FLAG_SPELLCLICK);
if (!creature)
continue;
creature->HandleSpellClick(bot);
return true;
}
}
else
{
GuidVector attackers = context->GetValue<GuidVector>("attackers")->Get();
Unit* target = nullptr;
for (auto i = attackers.begin(); i != attackers.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "death knight understudy"))
{
target = unit;
break;
}
}
if (target)
{
if (bot->GetDistance2d(target) > sPlayerbotAIConfig.spellDistance)
return MoveNear(target, sPlayerbotAIConfig.spellDistance, MovementPriority::MOVEMENT_COMBAT);
else
return botAI->CastSpell("mind control", target);
}
}
}
return false;
}
bool RazuviousTargetAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
Unit* razuvious = AI_VALUE2(Unit*, "find target", "instructor razuvious");
Unit* understudy = AI_VALUE2(Unit*, "find target", "death knight understudy");
Unit* target = nullptr;
if (botAI->IsTank(bot))
target = understudy;
else
target = razuvious;
if (AI_VALUE(Unit*, "current target") == target)
return false;
return Attack(target);
}

View File

@ -1,104 +0,0 @@
#include "RaidNaxxActions.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "RaidNaxxBossHelper.h"
#include "RaidNaxxSpellIds.h"
bool SapphironGroundPositionAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
if (botAI->IsMainTank(bot))
{
if (AI_VALUE2(bool, "has aggro", "current target"))
return MoveTo(NAXX_MAP_ID, helper.mainTankPos.first, helper.mainTankPos.second, helper.GENERIC_HEIGHT, false, false, false,
false, MovementPriority::MOVEMENT_COMBAT);
return false;
}
if (helper.JustLanded())
{
uint32 index = botAI->GetGroupSlotIndex(bot);
float start_angle = 0.85 * M_PI;
float offset_angle = M_PI * 0.02 * index;
float angle = start_angle + offset_angle;
float distance;
if (botAI->IsRanged(bot))
distance = 35.0f;
else if (botAI->IsHeal(bot))
distance = 30.0f;
else
distance = 5.0f;
float posX = helper.center.first + cos(angle) * distance;
float posY = helper.center.second + sin(angle) * distance;
if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
return true;
return MoveInside(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, 2.0f, MovementPriority::MOVEMENT_COMBAT);
}
else
{
std::vector<float> dest;
if (helper.FindPosToAvoidChill(dest))
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool SapphironFlightPositionAction::Execute(Event /*event*/)
{
if (!helper.UpdateBossAI())
return false;
if (helper.WaitForExplosion())
return MoveToNearestIcebolt();
else
{
std::vector<float> dest;
if (helper.FindPosToAvoidChill(dest))
return MoveTo(NAXX_MAP_ID, dest[0], dest[1], dest[2], false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool SapphironFlightPositionAction::MoveToNearestIcebolt()
{
Group* group = bot->GetGroup();
if (!group)
return false;
Group::MemberSlotList const& slots = group->GetMemberSlots();
Player* playerWithIcebolt = nullptr;
float minDistance;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) ||
botAI->HasAura("icebolt", member, false, false, -1, true))
{
if (!playerWithIcebolt || minDistance > bot->GetDistance(member))
{
playerWithIcebolt = member;
minDistance = bot->GetDistance(member);
}
}
}
if (playerWithIcebolt)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sapphiron");
if (boss)
{
float angle = boss->GetAngle(playerWithIcebolt);
float posX = playerWithIcebolt->GetPositionX() + cos(angle) * 3.0f;
float posY = playerWithIcebolt->GetPositionY() + sin(angle) * 3.0f;
if (MoveTo(NAXX_MAP_ID, posX, posY, helper.GENERIC_HEIGHT, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
return true;
return MoveNear(playerWithIcebolt, 3.0f, MovementPriority::MOVEMENT_COMBAT);
}
}
return false;
}

View File

@ -1,18 +0,0 @@
#include "RaidNaxxActions.h"
uint32 RotateAroundTheCenterPointAction::FindNearestWaypoint()
{
float minDistance = 0;
int ret = -1;
for (int i = 0; i < intervals; i++)
{
float w_x = waypoints[i].first, w_y = waypoints[i].second;
float dis = bot->GetDistance2d(w_x, w_y);
if (ret == -1 || dis < minDistance)
{
ret = i;
minDistance = dis;
}
}
return ret;
}

View File

@ -1,134 +0,0 @@
#include "RaidNaxxActions.h"
#include "PlayerbotAIConfig.h"
#include "Playerbots.h"
#include "RaidNaxxSpellIds.h"
bool ThaddiusAttackNearestPetAction::isUseful()
{
if (!helper.UpdateBossAI())
return false;
if (!helper.IsPhasePet())
return false;
Unit* target = helper.GetNearestPet();
if (!bot->IsWithinDistInMap(target, 50.0f))
return false;
return true;
}
bool ThaddiusAttackNearestPetAction::Execute(Event /*event*/)
{
Unit* target = helper.GetNearestPet();
if (!bot->IsWithinLOSInMap(target))
return MoveTo(target, 0, MovementPriority::MOVEMENT_COMBAT);
if (AI_VALUE(Unit*, "current target") != target)
return Attack(target);
if (botAI->IsTank(bot) && AI_VALUE2(bool, "has aggro", "current target"))
{
std::pair<float, float> posForTank = helper.PetPhaseGetPosForTank();
return MoveTo(533, posForTank.first, posForTank.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
if (botAI->IsRanged(bot))
{
std::pair<float, float> posForRanged = helper.PetPhaseGetPosForRanged();
return MoveTo(533, posForRanged.first, posForRanged.second, helper.tankPosZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool ThaddiusMoveToPlatformAction::isUseful() { return true; }
bool ThaddiusMoveToPlatformAction::Execute(Event /*event*/)
{
std::vector<std::pair<float, float>> position = {
// high left
{3462.99f, -2918.90f},
// high right
{3520.65f, -2976.51f},
// low left
{3471.36f, -2910.65f},
// low right
{3528.80f, -2967.04f},
// center
{3512.19f, -2928.58f},
};
float high_z = 312.00f, low_z = 304.02f;
bool is_left = bot->GetDistance2d(position[0].first, position[0].second) <
bot->GetDistance2d(position[1].first, position[1].second);
if (bot->GetPositionZ() >= (high_z - 3.0f))
{
if (is_left)
{
if (!MoveTo(bot->GetMapId(), position[0].first, position[0].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
float distance = bot->GetExactDist2d(position[0].first, position[0].second);
if (distance < sPlayerbotAIConfig.contactDistance)
JumpTo(bot->GetMapId(), position[2].first, position[2].second, low_z, MovementPriority::MOVEMENT_COMBAT);
// bot->TeleportTo(bot->GetMapId(), position[2].first, position[2].second, low_z, bot->GetOrientation());
}
}
else
{
if (!MoveTo(bot->GetMapId(), position[1].first, position[1].second, high_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT))
{
float distance = bot->GetExactDist2d(position[1].first, position[1].second);
if (distance < sPlayerbotAIConfig.contactDistance)
JumpTo(bot->GetMapId(), position[3].first, position[3].second, low_z, MovementPriority::MOVEMENT_COMBAT);
// bot->TeleportTo(bot->GetMapId(), position[3].first, position[3].second, low_z, bot->GetOrientation());
}
}
}
else
return MoveTo(bot->GetMapId(), position[4].first, position[4].second, low_z, false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
return true;
}
bool ThaddiusMovePolarityAction::isUseful()
{
return !botAI->IsMainTank(bot) || AI_VALUE2(bool, "has aggro", "current target");
}
bool ThaddiusMovePolarityAction::Execute(Event /*event*/)
{
std::vector<std::pair<float, float>> position = {
// left melee
{3508.29f, -2920.12f},
// left ranged
{3501.72f, -2913.36f},
// right melee
{3519.74f, -2931.69f},
// right ranged
{3524.32f, -2936.26f},
// center melee
{3512.19f, -2928.58f},
// center ranged
{3504.68f, -2936.68f},
};
uint32 idx;
if (NaxxSpellIds::HasAnyAura(
botAI, bot,
{NaxxSpellIds::NegativeCharge10, NaxxSpellIds::NegativeCharge25, NaxxSpellIds::NegativeChargeStack}) ||
botAI->HasAura("negative charge", bot, false, false, -1, true))
{
idx = 0;
}
else if (NaxxSpellIds::HasAnyAura(
botAI, bot,
{NaxxSpellIds::PositiveCharge10, NaxxSpellIds::PositiveCharge25, NaxxSpellIds::PositiveChargeStack}) ||
botAI->HasAura("positive charge", bot, false, false, -1, true))
{
idx = 1;
}
else
{
idx = 2;
}
idx = idx * 2 + botAI->IsRanged(bot);
return MoveTo(bot->GetMapId(), position[idx].first, position[idx].second, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}

View File

@ -1,322 +0,0 @@
#include "RaidNaxxMultipliers.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "FollowActions.h"
#include "GenericActions.h"
#include "GenericSpellActions.h"
#include "HunterActions.h"
#include "MageActions.h"
#include "MovementActions.h"
#include "PaladinActions.h"
#include "PriestActions.h"
#include "RaidNaxxActions.h"
#include "RaidNaxxSpellIds.h"
#include "ReachTargetActions.h"
#include "RogueActions.h"
#include "ScriptedCreature.h"
#include "ShamanActions.h"
#include "Spell.h"
#include "UseMeetingStoneAction.h"
#include "WarriorActions.h"
float GrobbulusMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
return 1.0f;
if (dynamic_cast<AvoidAoeAction*>(action))
return botAI->IsMainTank(bot) ? 0.0f : 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action))
return 0.0f;
return 1.0f;
}
//float HeiganDanceMultiplier::GetValue(Action* action)
//{
// Unit* boss = AI_VALUE2(Unit*, "find target", "heigan the unclean");
// if (!boss)
// {
// return 1.0f;
// }
// bool platform_phase = boss->IsWithinDist2d(2794.26f, -3706.67f, 10.0f);
// bool eruption_casting = false;
// if (boss->HasUnitState(UNIT_STATE_CASTING))
// {
// Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
// if (!spell)
// {
// spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
// }
// if (spell)
// {
// SpellInfo const* info = spell->GetSpellInfo();
// bool isEruption = NaxxSpellIds::MatchesAnySpellId(info, {NaxxSpellIds::Eruption10});
// if (!isEruption && info && info->SpellName[LOCALE_enUS])
// {
// // Fallback to name for custom spell data.
// isEruption = botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "eruption");
// }
// if (isEruption)
// {
// eruption_casting = true;
// }
// }
// }
// if (dynamic_cast<CombatFormationMoveAction*>(action) ||
// dynamic_cast<CastDisengageAction*>(action) ||
// dynamic_cast<CastBlinkBackAction*>(action) )
// {
// return 0.0f;
// }
// if (!platform_phase && !eruption_casting)
// {
// return 1.0f;
// }
// if (dynamic_cast<HeiganDanceAction*>(action) || dynamic_cast<CurePartyMemberAction*>(action))
// {
// return 1.0f;
// }
// if (dynamic_cast<CastSpellAction*>(action) && !dynamic_cast<CastMeleeSpellAction*>(action))
// {
// CastSpellAction* spellAction = dynamic_cast<CastSpellAction*>(action);
// uint32 spellId = AI_VALUE2(uint32, "spell id", spellAction->getSpell());
// SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
// if (!spellInfo)
// {
// return 0.0f;
// }
// uint32 castTime = spellInfo->CalcCastTime();
// if (castTime == 0 && !spellInfo->IsChanneled())
// {
// return 1.0f;
// }
// }
// return 0.0f;
//}
float LoathebGenericMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "loatheb");
if (!boss)
return 1.0f;
context->GetValue<bool>("neglect threat")->Set(true);
if (botAI->GetState() == BOT_STATE_COMBAT &&
(dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) || dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CombatFormationMoveAction*>(action)))
{
return 0.0f;
}
if (!dynamic_cast<CastHealingSpellAction*>(action))
return 1.0f;
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::NecroticAura10});
if (!aura)
{
// Fallback to name for custom spell data.
aura = botAI->GetAura("necrotic aura", bot);
}
if (!aura || aura->GetDuration() <= 1500)
return 1.0f;
return 0.0f;
}
float ThaddiusGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action))
return 0.0f;
// pet phase
if (helper.IsPhasePet() &&
(dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
dynamic_cast<ReachPartyMemberToHealAction*>(action) || dynamic_cast<BuffOnMainTankAction*>(action)))
{
return 0.0f;
}
// die at the same time
Unit* target = AI_VALUE(Unit*, "current target");
Unit* feugen = AI_VALUE2(Unit*, "find target", "feugen");
Unit* stalagg = AI_VALUE2(Unit*, "find target", "stalagg");
if (helper.IsPhasePet() && target && feugen && stalagg && target->GetHealthPct() <= 40 &&
(feugen->GetHealthPct() >= target->GetHealthPct() + 3 || stalagg->GetHealthPct() >= target->GetHealthPct() + 3))
{
if (dynamic_cast<CastSpellAction*>(action) && !dynamic_cast<CastHealingSpellAction*>(action))
return 0.0f;
}
// magnetic pull
// uint32 curr_timer = eventMap->GetTimer();
// // if (curr_phase == 2 && bot->GetPositionZ() > 312.5f && dynamic_cast<MovementAction*>(action))
// {
// if (curr_phase == 2 && (curr_timer % 20000 >= 18000 || curr_timer % 20000 <= 2000) &&
// dynamic_cast<MovementAction*>(action))
// {
// // MotionMaster *mm = bot->GetMotionMaster();
// // mm->Clear();
// return 0.0f;
// }
// thaddius phase
// if (curr_phase == 8 && dynamic_cast<FleeAction*>(action))
// {
// return 0.0f;
// }
return 1.0f;
}
float SapphironGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
return 1.0f;
if (dynamic_cast<CastDeathGripAction*>(action) || dynamic_cast<CombatFormationMoveAction*>(action))
return 0.0f;
return 1.0f;
}
float InstructorRazuviousGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
return 1.0f;
context->GetValue<bool>("neglect threat")->Set(true);
if (botAI->GetState() == BOT_STATE_COMBAT &&
(dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action)))
{
return 0.0f;
}
return 1.0f;
}
float KelthuzadGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
return 1.0f;
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) || dynamic_cast<FleeAction*>(action)))
{
return 0.0f;
}
if (helper.IsPhaseOne())
{
if (dynamic_cast<CastTotemAction*>(action) || dynamic_cast<CastShadowfiendAction*>(action) ||
dynamic_cast<CastRaiseDeadAction*>(action) || dynamic_cast<CastFeignDeathAction*>(action) ||
dynamic_cast<CastInvisibilityAction*>(action) || dynamic_cast<CastVanishAction*>(action) ||
dynamic_cast<PetAttackAction*>(action))
{
return 0.0f;
}
}
if (helper.IsPhaseTwo())
{
if (dynamic_cast<CastBlizzardAction*>(action) || dynamic_cast<CastFrostNovaAction*>(action))
return 0.0f;
}
return 1.0f;
}
float AnubrekhanGenericMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
if (!boss)
return 1.0f;
if (NaxxSpellIds::HasAnyAura(
botAI, boss, {NaxxSpellIds::LocustSwarm10, NaxxSpellIds::LocustSwarm10Alt, NaxxSpellIds::LocustSwarm25}) ||
botAI->HasAura("locust swarm", boss))
{
if (dynamic_cast<FleeAction*>(action))
return 0.0f;
}
return 1.0f;
}
float FourhorsemanGenericMultiplier::GetValue(Action* action)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "sir zeliek");
if (!boss)
return 1.0f;
context->GetValue<bool>("neglect threat")->Set(true);
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action)))
return 0.0f;
return 1.0f;
}
// float GothikGenericMultiplier::GetValue(Action* action)
// {
// Unit* boss = AI_VALUE2(Unit*, "find target", "gothik the harvester");
// if (!boss)
// {
// return 1.0f;
// }
// BossAI* boss_ai = dynamic_cast<BossAI*>(boss->GetAI());
// EventMap* eventMap = boss_botAI->GetEvents();
// uint32 curr_phase = eventMap->GetPhaseMask();
// if (curr_phase == 1 && (dynamic_cast<FollowAction*>(action)))
// {
// return 0.0f;
// }
// if (curr_phase == 1 && (dynamic_cast<AttackAction*>(action)))
// {
// Unit* target = action->GetTarget();
// if (target == boss)
// {
// return 0.0f;
// }
// }
// return 1.0f;
// }
float GluthGenericMultiplier::GetValue(Action* action)
{
if (!helper.UpdateBossAI())
return 1.0f;
if ((dynamic_cast<DpsAssistAction*>(action) || dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<FleeAction*>(action) || dynamic_cast<CastDebuffSpellOnAttackerAction*>(action) ||
dynamic_cast<CastStarfallAction*>(action)))
{
return 0.0f;
}
if (botAI->IsMainTank(bot))
{
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25});
if (!aura)
{
// Fallback to name for custom spell data.
aura = botAI->GetAura("mortal wound", bot, false, true);
}
if (aura && aura->GetStackAmount() >= 5)
{
if (dynamic_cast<CastTauntAction*>(action) || dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) || dynamic_cast<CastGrowlAction*>(action))
{
return 0.0f;
}
}
}
if (dynamic_cast<PetAttackAction*>(action))
{
Unit* target = AI_VALUE(Unit*, "current target");
if (helper.IsZombieChow(target))
return 0.0f;
}
return 1.0f;
}

View File

@ -1,115 +0,0 @@
#ifndef _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
#define _PLAYERRBOT_RAIDNAXXMULTIPLIERS_H
#include "Multiplier.h"
#include "RaidNaxxBossHelper.h"
class GrobbulusMultiplier : public Multiplier
{
public:
GrobbulusMultiplier(PlayerbotAI* ai) : Multiplier(ai, "grobbulus") {}
public:
virtual float GetValue(Action* action);
};
//class HeiganDanceMultiplier : public Multiplier
//{
//public:
// HeiganDanceMultiplier(PlayerbotAI* ai) : Multiplier(ai, "helgan dance") {}
//
//public:
// virtual float GetValue(Action* action);
//};
class LoathebGenericMultiplier : public Multiplier
{
public:
LoathebGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loatheb generic") {}
public:
virtual float GetValue(Action* action);
};
class ThaddiusGenericMultiplier : public Multiplier
{
public:
ThaddiusGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "thaddius generic"), helper(ai) {}
public:
virtual float GetValue(Action* action);
private:
ThaddiusBossHelper helper;
};
class SapphironGenericMultiplier : public Multiplier
{
public:
SapphironGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sapphiron generic"), helper(ai) {}
virtual float GetValue(Action* action);
private:
SapphironBossHelper helper;
};
class InstructorRazuviousGenericMultiplier : public Multiplier
{
public:
InstructorRazuviousGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "instructor razuvious generic"), helper(ai) {}
virtual float GetValue(Action* action);
private:
RazuviousBossHelper helper;
};
class KelthuzadGenericMultiplier : public Multiplier
{
public:
KelthuzadGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "kelthuzad generic"), helper(ai) {}
virtual float GetValue(Action* action);
private:
KelthuzadBossHelper helper;
};
class AnubrekhanGenericMultiplier : public Multiplier
{
public:
AnubrekhanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "anubrekhan generic") {}
public:
virtual float GetValue(Action* action);
};
class FourhorsemanGenericMultiplier : public Multiplier
{
public:
FourhorsemanGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "fourhorseman generic") {}
public:
virtual float GetValue(Action* action);
};
// class GothikGenericMultiplier : public Multiplier
// {
// public:
// GothikGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gothik generic") {}
// public:
// virtual float GetValue(Action* action);
// };
class GluthGenericMultiplier : public Multiplier
{
public:
GluthGenericMultiplier(PlayerbotAI* ai) : Multiplier(ai, "gluth generic"), helper(ai) {}
float GetValue(Action* action) override;
private:
GluthBossHelper helper;
};
#endif

View File

@ -1,95 +0,0 @@
// /*
// * 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_RAIDNAXXACTIONCONTEXT_H
#define _PLAYERBOT_RAIDNAXXACTIONCONTEXT_H
#include "Action.h"
#include "NamedObjectContext.h"
#include "RaidNaxxActions.h"
class RaidNaxxActionContext : public NamedObjectContext<Action>
{
public:
RaidNaxxActionContext()
{
creators["grobbulus go behind the boss"] = &RaidNaxxActionContext::go_behind_the_boss;
creators["rotate grobbulus"] = &RaidNaxxActionContext::rotate_grobbulus;
creators["grobbulus move center"] = &RaidNaxxActionContext::grobbulus_move_center;
creators["grobbulus move away"] = &RaidNaxxActionContext::grobbulus_move_away;
//creators["heigan dance melee"] = &RaidNaxxActionContext::heigan_dance_melee;
//creators["heigan dance ranged"] = &RaidNaxxActionContext::heigan_dance_ranged;
creators["thaddius attack nearest pet"] = &RaidNaxxActionContext::thaddius_attack_nearest_pet;
// creators["thaddius melee to place"] = &RaidNaxxActionContext::thaddius_tank_to_place;
// creators["thaddius ranged to place"] = &RaidNaxxActionContext::thaddius_ranged_to_place;
creators["thaddius move to platform"] = &RaidNaxxActionContext::thaddius_move_to_platform;
creators["thaddius move polarity"] = &RaidNaxxActionContext::thaddius_move_polarity;
creators["razuvious use obedience crystal"] = &RaidNaxxActionContext::razuvious_use_obedience_crystal;
creators["razuvious target"] = &RaidNaxxActionContext::razuvious_target;
creators["horseman attract alternatively"] = &RaidNaxxActionContext::horseman_attract_alternatively;
creators["horseman attack in order"] = &RaidNaxxActionContext::horseman_attack_in_order;
creators["sapphiron ground position"] = &RaidNaxxActionContext::sapphiron_ground_position;
creators["sapphiron flight position"] = &RaidNaxxActionContext::sapphiron_flight_position;
creators["kel'thuzad choose target"] = &RaidNaxxActionContext::kelthuzad_choose_target;
creators["kel'thuzad position"] = &RaidNaxxActionContext::kelthuzad_position;
creators["anub'rekhan choose target"] = &RaidNaxxActionContext::anubrekhan_choose_target;
creators["anub'rekhan position"] = &RaidNaxxActionContext::anubrekhan_position;
creators["gluth choose target"] = &RaidNaxxActionContext::gluth_choose_target;
creators["gluth position"] = &RaidNaxxActionContext::gluth_position;
creators["gluth slowdown"] = &RaidNaxxActionContext::gluth_slowdown;
//creators["patchwerk ranged position"] = &RaidNaxxActionContext::patchwerk_ranged_position;
creators["loatheb position"] = &RaidNaxxActionContext::loatheb_position;
creators["loatheb choose target"] = &RaidNaxxActionContext::loatheb_choose_target;
}
private:
static Action* go_behind_the_boss(PlayerbotAI* ai) { return new GrobbulusGoBehindAction(ai); }
static Action* rotate_grobbulus(PlayerbotAI* ai) { return new GrobbulusRotateAction(ai); }
static Action* grobbulus_move_center(PlayerbotAI* ai) { return new GrobblulusMoveCenterAction(ai); }
static Action* grobbulus_move_away(PlayerbotAI* ai) { return new GrobbulusMoveAwayAction(ai); }
//static Action* heigan_dance_melee(PlayerbotAI* ai) { return new HeiganDanceMeleeAction(ai); }
//static Action* heigan_dance_ranged(PlayerbotAI* ai) { return new HeiganDanceRangedAction(ai); }
static Action* thaddius_attack_nearest_pet(PlayerbotAI* ai) { return new ThaddiusAttackNearestPetAction(ai); }
// static Action* thaddius_tank_to_place(PlayerbotAI* ai) { return new ThaddiusMeleeToPlaceAction(ai); }
// static Action* thaddius_ranged_to_place(PlayerbotAI* ai) { return new ThaddiusRangedToPlaceAction(ai); }
static Action* thaddius_move_to_platform(PlayerbotAI* ai) { return new ThaddiusMoveToPlatformAction(ai); }
static Action* thaddius_move_polarity(PlayerbotAI* ai) { return new ThaddiusMovePolarityAction(ai); }
static Action* razuvious_target(PlayerbotAI* ai) { return new RazuviousTargetAction(ai); }
static Action* razuvious_use_obedience_crystal(PlayerbotAI* ai)
{
return new RazuviousUseObedienceCrystalAction(ai);
}
static Action* horseman_attract_alternatively(PlayerbotAI* ai)
{
return new HorsemanAttractAlternativelyAction(ai);
}
static Action* horseman_attack_in_order(PlayerbotAI* ai) { return new HorsemanAttactInOrderAction(ai); }
// static Action* sapphiron_ground_main_tank_position(PlayerbotAI* ai) { return new
// SapphironGroundMainTankPositionAction(ai); }
static Action* sapphiron_ground_position(PlayerbotAI* ai) { return new SapphironGroundPositionAction(ai); }
static Action* sapphiron_flight_position(PlayerbotAI* ai) { return new SapphironFlightPositionAction(ai); }
// static Action* sapphiron_avoid_chill(PlayerbotAI* ai) { return new SapphironAvoidChillAction(ai); }
static Action* kelthuzad_choose_target(PlayerbotAI* ai) { return new KelthuzadChooseTargetAction(ai); }
static Action* kelthuzad_position(PlayerbotAI* ai) { return new KelthuzadPositionAction(ai); }
static Action* anubrekhan_choose_target(PlayerbotAI* ai) { return new AnubrekhanChooseTargetAction(ai); }
static Action* anubrekhan_position(PlayerbotAI* ai) { return new AnubrekhanPositionAction(ai); }
static Action* gluth_choose_target(PlayerbotAI* ai) { return new GluthChooseTargetAction(ai); }
static Action* gluth_position(PlayerbotAI* ai) { return new GluthPositionAction(ai); }
static Action* gluth_slowdown(PlayerbotAI* ai) { return new GluthSlowdownAction(ai); }
//static Action* patchwerk_ranged_position(PlayerbotAI* ai) { return new PatchwerkRangedPositionAction(ai); }
static Action* loatheb_position(PlayerbotAI* ai) { return new LoathebPositionAction(ai); }
static Action* loatheb_choose_target(PlayerbotAI* ai) { return new LoathebChooseTargetAction(ai); }
};
#endif

View File

@ -1,86 +0,0 @@
// /*
// * 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_RAIDNAXXTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDNAXXTRIGGERCONTEXT_H
#include "AiObjectContext.h"
#include "NamedObjectContext.h"
#include "RaidNaxxTriggers.h"
class RaidNaxxTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidNaxxTriggerContext()
{
creators["mutating injection melee"] = &RaidNaxxTriggerContext::mutating_injection_melee;
creators["mutating injection ranged"] = &RaidNaxxTriggerContext::mutating_injection_ranged;
creators["mutating injection removed"] = &RaidNaxxTriggerContext::mutating_injection_removed;
creators["grobbulus cloud"] = &RaidNaxxTriggerContext::grobbulus_cloud;
//creators["heigan melee"] = &RaidNaxxTriggerContext::heigan_melee;
//creators["heigan ranged"] = &RaidNaxxTriggerContext::heigan_ranged;
creators["thaddius phase pet"] = &RaidNaxxTriggerContext::thaddius_phase_pet;
creators["thaddius phase pet lose aggro"] = &RaidNaxxTriggerContext::thaddius_phase_pet_lose_aggro;
creators["thaddius phase transition"] = &RaidNaxxTriggerContext::thaddius_phase_transition;
creators["thaddius phase thaddius"] = &RaidNaxxTriggerContext::thaddius_phase_thaddius;
creators["razuvious tank"] = &RaidNaxxTriggerContext::razuvious_tank;
creators["razuvious nontank"] = &RaidNaxxTriggerContext::razuvious_nontank;
creators["horseman attractors"] = &RaidNaxxTriggerContext::horseman_attractors;
creators["horseman except attractors"] = &RaidNaxxTriggerContext::horseman_except_attractors;
creators["sapphiron ground"] = &RaidNaxxTriggerContext::sapphiron_ground;
creators["sapphiron flight"] = &RaidNaxxTriggerContext::sapphiron_flight;
creators["kel'thuzad"] = &RaidNaxxTriggerContext::kelthuzad;
creators["anub'rekhan"] = &RaidNaxxTriggerContext::anubrekhan;
creators["faerlina"] = &RaidNaxxTriggerContext::faerlina;
creators["maexxna"] = &RaidNaxxTriggerContext::maexxna;
//creators["patchwerk tank"] = &RaidNaxxTriggerContext::patchwerk_tank;
//creators["patchwerk non-tank"] = &RaidNaxxTriggerContext::patchwerk_non_tank;
//creators["patchwerk ranged"] = &RaidNaxxTriggerContext::patchwerk_ranged;
creators["gluth"] = &RaidNaxxTriggerContext::gluth;
creators["gluth main tank mortal wound"] = &RaidNaxxTriggerContext::gluth_main_tank_mortal_wound;
creators["loatheb"] = &RaidNaxxTriggerContext::loatheb;
}
private:
static Trigger* mutating_injection_melee(PlayerbotAI* ai) { return new MutatingInjectionMeleeTrigger(ai); }
static Trigger* mutating_injection_ranged(PlayerbotAI* ai) { return new MutatingInjectionRangedTrigger(ai); }
static Trigger* mutating_injection_removed(PlayerbotAI* ai) { return new MutatingInjectionRemovedTrigger(ai); }
static Trigger* grobbulus_cloud(PlayerbotAI* ai) { return new GrobbulusCloudTrigger(ai); }
//static Trigger* heigan_melee(PlayerbotAI* ai) { return new HeiganMeleeTrigger(ai); }
//static Trigger* heigan_ranged(PlayerbotAI* ai) { return new HeiganRangedTrigger(ai); }
static Trigger* thaddius_phase_pet(PlayerbotAI* ai) { return new ThaddiusPhasePetTrigger(ai); }
static Trigger* thaddius_phase_pet_lose_aggro(PlayerbotAI* ai) { return new ThaddiusPhasePetLoseAggroTrigger(ai); }
static Trigger* thaddius_phase_transition(PlayerbotAI* ai) { return new ThaddiusPhaseTransitionTrigger(ai); }
static Trigger* thaddius_phase_thaddius(PlayerbotAI* ai) { return new ThaddiusPhaseThaddiusTrigger(ai); }
static Trigger* razuvious_tank(PlayerbotAI* ai) { return new RazuviousTankTrigger(ai); }
static Trigger* razuvious_nontank(PlayerbotAI* ai) { return new RazuviousNontankTrigger(ai); }
static Trigger* horseman_attractors(PlayerbotAI* ai) { return new HorsemanAttractorsTrigger(ai); }
static Trigger* horseman_except_attractors(PlayerbotAI* ai) { return new HorsemanExceptAttractorsTrigger(ai); }
static Trigger* sapphiron_ground(PlayerbotAI* ai) { return new SapphironGroundTrigger(ai); }
static Trigger* sapphiron_flight(PlayerbotAI* ai) { return new SapphironFlightTrigger(ai); }
static Trigger* kelthuzad(PlayerbotAI* ai) { return new KelthuzadTrigger(ai); }
static Trigger* anubrekhan(PlayerbotAI* ai) { return new AnubrekhanTrigger(ai); }
static Trigger* faerlina(PlayerbotAI* ai) { return new FaerlinaTrigger(ai); }
static Trigger* maexxna(PlayerbotAI* ai) { return new MaexxnaTrigger(ai); }
//static Trigger* patchwerk_tank(PlayerbotAI* ai) { return new PatchwerkTankTrigger(ai); }
//static Trigger* patchwerk_non_tank(PlayerbotAI* ai) { return new PatchwerkNonTankTrigger(ai); }
//static Trigger* patchwerk_ranged(PlayerbotAI* ai) { return new PatchwerkRangedTrigger(ai); }
static Trigger* gluth(PlayerbotAI* ai) { return new GluthTrigger(ai); }
static Trigger* gluth_main_tank_mortal_wound(PlayerbotAI* ai) { return new GluthMainTankMortalWoundTrigger(ai); }
static Trigger* loatheb(PlayerbotAI* ai) { return new LoathebTrigger(ai); }
};
#endif

View File

@ -1,156 +0,0 @@
#include "RaidNaxxStrategy.h"
#include "RaidNaxxMultipliers.h"
void RaidNaxxStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// Grobbulus
triggers.push_back(new TriggerNode("mutating injection melee",
{ NextAction("grobbulus move away", ACTION_RAID + 2) }
));
triggers.push_back(new TriggerNode("mutating injection ranged",
{ NextAction("grobbulus go behind the boss", ACTION_RAID + 2) }
));
triggers.push_back(new TriggerNode("mutating injection removed",
{ NextAction("grobbulus move center", ACTION_RAID + 1) }
));
triggers.push_back(new TriggerNode("grobbulus cloud",
{ NextAction("rotate grobbulus", ACTION_RAID + 1) }
));
// Heigan the Unclean
//triggers.push_back(new TriggerNode("heigan melee",
// { NextAction("heigan dance melee", ACTION_RAID + 1) }
//));
//triggers.push_back(new TriggerNode("heigan ranged",
// { NextAction("heigan dance ranged", ACTION_RAID + 1) }
//));
// Kel'Thuzad
triggers.push_back(
new TriggerNode("kel'thuzad",
{
NextAction("kel'thuzad position", ACTION_RAID + 2),
NextAction("kel'thuzad choose target", ACTION_RAID + 1)
})
);
// Anub'Rekhan
triggers.push_back(new TriggerNode("anub'rekhan",
{ NextAction("anub'rekhan position", ACTION_RAID + 1) }
));
// Grand Widow Faerlina
triggers.push_back(new TriggerNode("faerlina",
{ NextAction("avoid aoe", ACTION_RAID + 1) }
));
// Maexxna
triggers.push_back(
new TriggerNode("maexxna",
{
NextAction("rear flank", ACTION_RAID + 1),
NextAction("avoid aoe", ACTION_RAID + 1)
})
);
// Patchwerk
//triggers.push_back(new TriggerNode("patchwerk tank",
// { NextAction("tank face", ACTION_RAID + 2) }
//));
//triggers.push_back(new TriggerNode("patchwerk ranged",
// { NextAction("patchwerk ranged position", ACTION_RAID + 2) }
//));
//triggers.push_back(new TriggerNode("patchwerk non-tank",
// { NextAction("rear flank", ACTION_RAID + 1) }
//));
// Thaddius
triggers.push_back(new TriggerNode("thaddius phase pet",
{ NextAction("thaddius attack nearest pet", ACTION_RAID + 1) }
));
triggers.push_back(new TriggerNode("thaddius phase pet lose aggro",
{ NextAction("taunt spell", ACTION_RAID + 2) }
));
triggers.push_back(new TriggerNode("thaddius phase transition",
{ NextAction("thaddius move to platform", ACTION_RAID + 1) }
));
triggers.push_back(new TriggerNode("thaddius phase thaddius",
{ NextAction("thaddius move polarity", ACTION_RAID + 1) }
));
// Instructor Razuvious
triggers.push_back(new TriggerNode("razuvious tank",
{ NextAction("razuvious use obedience crystal", ACTION_RAID + 1) }
));
triggers.push_back(new TriggerNode("razuvious nontank",
{ NextAction("razuvious target", ACTION_RAID + 1) }
));
// four horseman
triggers.push_back(new TriggerNode("horseman attractors",
{ NextAction("horseman attract alternatively", ACTION_RAID + 1) }
));
triggers.push_back(new TriggerNode("horseman except attractors",
{ NextAction("horseman attack in order", ACTION_RAID + 1) }
));
// sapphiron
triggers.push_back(new TriggerNode("sapphiron ground",
{ NextAction("sapphiron ground position", ACTION_RAID + 1) }
));
triggers.push_back(new TriggerNode("sapphiron flight",
{ NextAction("sapphiron flight position", ACTION_RAID + 1) }
));
// Gluth
triggers.push_back(
new TriggerNode("gluth",
{
NextAction("gluth choose target", ACTION_RAID + 1),
NextAction("gluth position", ACTION_RAID + 1),
NextAction("gluth slowdown", ACTION_RAID)
})
);
triggers.push_back(new TriggerNode("gluth main tank mortal wound",
{ NextAction("taunt spell", ACTION_RAID + 1) }
));
// Loatheb
triggers.push_back(
new TriggerNode("loatheb",
{
NextAction("loatheb position", ACTION_RAID + 1),
NextAction("loatheb choose target", ACTION_RAID + 1)
})
);
}
void RaidNaxxStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new GrobbulusMultiplier(botAI));
//multipliers.push_back(new HeiganDanceMultiplier(botAI));
multipliers.push_back(new LoathebGenericMultiplier(botAI));
multipliers.push_back(new ThaddiusGenericMultiplier(botAI));
multipliers.push_back(new SapphironGenericMultiplier(botAI));
multipliers.push_back(new InstructorRazuviousGenericMultiplier(botAI));
multipliers.push_back(new KelthuzadGenericMultiplier(botAI));
multipliers.push_back(new AnubrekhanGenericMultiplier(botAI));
multipliers.push_back(new FourhorsemanGenericMultiplier(botAI));
// multipliers.push_back(new GothikGenericMultiplier(botAI));
multipliers.push_back(new GluthGenericMultiplier(botAI));
}

View File

@ -1,18 +0,0 @@
#ifndef _PLAYERBOT_RAIDNAXXSTRATEGY_H
#define _PLAYERBOT_RAIDNAXXSTRATEGY_H
#include "AiObjectContext.h"
#include "Multiplier.h"
#include "Strategy.h"
class RaidNaxxStrategy : public Strategy
{
public:
RaidNaxxStrategy(PlayerbotAI* ai) : Strategy(ai) {}
virtual std::string const getName() override { return "naxx"; }
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@ -1,257 +0,0 @@
#include "RaidNaxxTriggers.h"
#include "Playerbots.h"
#include "RaidNaxxSpellIds.h"
#include "Timer.h"
#include "Trigger.h"
bool MutatingInjectionMeleeTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
return false;
return MutatingInjectionTrigger::IsActive() && !botAI->IsRanged(bot);
}
bool MutatingInjectionRangedTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
return false;
return MutatingInjectionTrigger::IsActive() && botAI->IsRanged(bot);
}
bool AuraRemovedTrigger::IsActive()
{
bool check = botAI->HasAura(name, bot, false, false, -1, true);
bool ret = false;
if (prev_check && !check)
ret = true;
prev_check = check;
return ret;
}
bool MutatingInjectionRemovedTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
return false;
return HasNoAuraTrigger::IsActive() && botAI->GetState() == BOT_STATE_COMBAT && botAI->IsRanged(bot);
}
bool GrobbulusCloudTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grobbulus");
if (!boss)
return false;
if (!botAI->IsMainTank(bot))
return false;
// bot->Yell("has aggro on " + boss->GetName() + " : " + to_string(AI_VALUE2(bool, "has aggro", "boss target")),
// LANG_UNIVERSAL);
if (!AI_VALUE2(bool, "has aggro", "boss target"))
return false;
uint32 now = getMSTime();
bool poison_cloud_casting = false;
if (boss->HasUnitState(UNIT_STATE_CASTING))
{
Spell* spell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!spell)
spell = boss->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
if (spell)
poison_cloud_casting = NaxxSpellIds::MatchesAnySpellId(spell->GetSpellInfo(), {NaxxSpellIds::PoisonCloud});
}
if (!poison_cloud_casting && last_cloud_ms != 0 && now - last_cloud_ms < CloudRotationDelayMs)
return false;
last_cloud_ms = now;
return true;
}
//bool HeiganMeleeTrigger::IsActive()
//{
// Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
// if (!heigan)
// {
// return false;
// }
// return !botAI->IsRanged(bot);
//}
//
//bool HeiganRangedTrigger::IsActive()
//{
// Unit* heigan = AI_VALUE2(Unit*, "find target", "heigan the unclean");
// if (!heigan)
// {
// return false;
// }
// return botAI->IsRanged(bot);
//}
bool RazuviousTankTrigger::IsActive()
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
return helper.UpdateBossAI() && botAI->IsTank(bot);
return helper.UpdateBossAI() && bot->getClass() == CLASS_PRIEST;
}
bool RazuviousNontankTrigger::IsActive()
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_10MAN_NORMAL)
return helper.UpdateBossAI() && !(botAI->IsTank(bot));
return helper.UpdateBossAI() && !(bot->getClass() == CLASS_PRIEST);
}
bool HorsemanAttractorsTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return helper.IsAttracter(bot);
}
bool HorsemanExceptAttractorsTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return !helper.IsAttracter(bot);
}
bool SapphironGroundTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return helper.IsPhaseGround();
}
bool SapphironFlightTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return helper.IsPhaseFlight();
}
bool GluthTrigger::IsActive() { return helper.UpdateBossAI(); }
bool GluthMainTankMortalWoundTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
if (!botAI->IsAssistTankOfIndex(bot, 0))
return false;
Unit* mt = AI_VALUE(Unit*, "main tank");
if (!mt)
return false;
Aura* aura = NaxxSpellIds::GetAnyAura(mt, {NaxxSpellIds::MortalWound10, NaxxSpellIds::MortalWound25});
if (!aura)
{
// Fallback to name for custom spell data.
aura = botAI->GetAura("mortal wound", mt, false, true);
}
if (!aura || aura->GetStackAmount() < 5)
return false;
return true;
}
bool KelthuzadTrigger::IsActive() { return helper.UpdateBossAI(); }
bool AnubrekhanTrigger::IsActive() {
Unit* boss = AI_VALUE2(Unit*, "find target", "anub'rekhan");
if (!boss)
return false;
return true;
}
bool FaerlinaTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "grand widow faerlina");
if (!boss)
return false;
return true;
}
bool MaexxnaTrigger::IsActive()
{
Unit* boss = AI_VALUE2(Unit*, "find target", "maexxna");
if (!boss)
return false;
return !botAI->IsTank(bot);
}
//bool PatchwerkTankTrigger::IsActive()
//{
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
// if (!boss)
// {
// return false;
// }
// return !botAI->IsTank(bot) && !botAI->IsRanged(bot);
//}
//
//bool PatchwerkRangedTrigger::IsActive()
//{
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
// if (!boss)
// {
// return false;
// }
// return !botAI->IsTank(bot) && botAI->IsRanged(bot);
//}
//
//bool PatchwerkNonTankTrigger::IsActive()
//{
// Unit* boss = AI_VALUE2(Unit*, "find target", "patchwerk");
// if (!boss)
// {
// return false;
// }
// return !botAI->IsTank(bot);
//}
bool LoathebTrigger::IsActive() { return helper.UpdateBossAI(); }
bool ThaddiusPhasePetTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return helper.IsPhasePet();
}
bool ThaddiusPhaseTransitionTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return helper.IsPhaseTransition();
}
bool ThaddiusPhaseThaddiusTrigger::IsActive()
{
if (!helper.UpdateBossAI())
return false;
return helper.IsPhaseThaddius();
}

View File

@ -1,259 +0,0 @@
#ifndef _PLAYERBOT_RAIDNAXXTRIGGERS_H
#define _PLAYERBOT_RAIDNAXXTRIGGERS_H
#include "EventMap.h"
#include "GenericTriggers.h"
#include "PlayerbotAIConfig.h"
#include "RaidNaxxBossHelper.h"
#include "Trigger.h"
class MutatingInjectionTrigger : public HasAuraTrigger
{
public:
MutatingInjectionTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "mutating injection", 1) {}
};
class MutatingInjectionMeleeTrigger : public MutatingInjectionTrigger
{
public:
MutatingInjectionMeleeTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {}
bool IsActive() override;
};
class MutatingInjectionRangedTrigger : public MutatingInjectionTrigger
{
public:
MutatingInjectionRangedTrigger(PlayerbotAI* ai) : MutatingInjectionTrigger(ai) {}
bool IsActive() override;
};
class AuraRemovedTrigger : public Trigger
{
public:
AuraRemovedTrigger(PlayerbotAI* botAI, std::string name) : Trigger(botAI, name, 1)
{
this->prev_check = false;
}
virtual bool IsActive() override;
protected:
bool prev_check;
};
class MutatingInjectionRemovedTrigger : public HasNoAuraTrigger
{
public:
MutatingInjectionRemovedTrigger(PlayerbotAI* ai) : HasNoAuraTrigger(ai, "mutating injection") {}
virtual bool IsActive();
};
class GrobbulusCloudTrigger : public Trigger
{
public:
GrobbulusCloudTrigger(PlayerbotAI* ai) : Trigger(ai, "grobbulus cloud event"), last_cloud_ms(0) {}
bool IsActive() override;
private:
uint32 last_cloud_ms;
static constexpr uint32 CloudRotationDelayMs = 15000;
};
//class HeiganMeleeTrigger : public Trigger
//{
//public:
// HeiganMeleeTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan melee") {}
// virtual bool IsActive();
//};
//
//class HeiganRangedTrigger : public Trigger
//{
//public:
// HeiganRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "heigan ranged") {}
// bool IsActive() override;
//};
class RazuviousTankTrigger : public Trigger
{
public:
RazuviousTankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious tank"), helper(ai) {}
bool IsActive() override;
private:
RazuviousBossHelper helper;
};
class RazuviousNontankTrigger : public Trigger
{
public:
RazuviousNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "instructor razuvious non-tank"), helper(ai) {}
bool IsActive() override;
private:
RazuviousBossHelper helper;
};
class KelthuzadTrigger : public Trigger
{
public:
KelthuzadTrigger(PlayerbotAI* ai) : Trigger(ai, "kel'thuzad trigger"), helper(ai) {}
bool IsActive() override;
private:
KelthuzadBossHelper helper;
};
class AnubrekhanTrigger : public Trigger
{
public:
AnubrekhanTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'rekhan") {}
bool IsActive() override;
};
class FaerlinaTrigger : public Trigger
{
public:
FaerlinaTrigger(PlayerbotAI* ai) : Trigger(ai, "faerlina") {}
bool IsActive() override;
};
class MaexxnaTrigger : public Trigger
{
public:
MaexxnaTrigger(PlayerbotAI* ai) : Trigger(ai, "maexxna") {}
bool IsActive() override;
};
//class PatchwerkTankTrigger : public Trigger
//{
//public:
// PatchwerkTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk tank") {}
// bool IsActive() override;
//};
//
//class PatchwerkNonTankTrigger : public Trigger
//{
//public:
// PatchwerkNonTankTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk non-tank") {}
// bool IsActive() override;
//};
//
//class PatchwerkRangedTrigger : public Trigger
//{
//public:
// PatchwerkRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "patchwerk ranged") {}
// bool IsActive() override;
//};
class ThaddiusPhasePetTrigger : public Trigger
{
public:
ThaddiusPhasePetTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase pet"), helper(ai) {}
bool IsActive() override;
private:
ThaddiusBossHelper helper;
};
class ThaddiusPhasePetLoseAggroTrigger : public ThaddiusPhasePetTrigger
{
public:
ThaddiusPhasePetLoseAggroTrigger(PlayerbotAI* ai) : ThaddiusPhasePetTrigger(ai) {}
virtual bool IsActive()
{
Unit* target = AI_VALUE(Unit*, "current target");
return ThaddiusPhasePetTrigger::IsActive() && botAI->IsTank(bot) && target && target->GetVictim() != bot;
}
};
class ThaddiusPhaseTransitionTrigger : public Trigger
{
public:
ThaddiusPhaseTransitionTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase transition"), helper(ai) {}
bool IsActive() override;
private:
ThaddiusBossHelper helper;
};
class ThaddiusPhaseThaddiusTrigger : public Trigger
{
public:
ThaddiusPhaseThaddiusTrigger(PlayerbotAI* ai) : Trigger(ai, "thaddius phase thaddius"), helper(ai) {}
bool IsActive() override;
private:
ThaddiusBossHelper helper;
};
class HorsemanAttractorsTrigger : public Trigger
{
public:
HorsemanAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen attractors"), helper(ai) {}
bool IsActive() override;
private:
FourhorsemanBossHelper helper;
};
class HorsemanExceptAttractorsTrigger : public Trigger
{
public:
HorsemanExceptAttractorsTrigger(PlayerbotAI* ai) : Trigger(ai, "fourhorsemen except attractors"), helper(ai) {}
bool IsActive() override;
private:
FourhorsemanBossHelper helper;
};
class SapphironGroundTrigger : public Trigger
{
public:
SapphironGroundTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron ground"), helper(ai) {}
bool IsActive() override;
private:
SapphironBossHelper helper;
};
class SapphironFlightTrigger : public Trigger
{
public:
SapphironFlightTrigger(PlayerbotAI* ai) : Trigger(ai, "sapphiron flight"), helper(ai) {}
bool IsActive() override;
private:
SapphironBossHelper helper;
};
class GluthTrigger : public Trigger
{
public:
GluthTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth trigger"), helper(ai) {}
bool IsActive() override;
private:
GluthBossHelper helper;
};
class GluthMainTankMortalWoundTrigger : public Trigger
{
public:
GluthMainTankMortalWoundTrigger(PlayerbotAI* ai) : Trigger(ai, "gluth main tank mortal wound trigger"), helper(ai) {}
bool IsActive() override;
private:
GluthBossHelper helper;
};
class LoathebTrigger : public Trigger
{
public:
LoathebTrigger(PlayerbotAI* ai) : Trigger(ai, "loatheb"), helper(ai) {}
bool IsActive() override;
private:
LoathebBossHelper helper;
};
#endif

View File

@ -1,533 +0,0 @@
#ifndef _PLAYERBOT_RAIDNAXXBOSSHELPER_H
#define _PLAYERBOT_RAIDNAXXBOSSHELPER_H
#include <string>
#include "AiObject.h"
#include "AiObjectContext.h"
#include "EventMap.h"
#include "Log.h"
#include "NamedObjectContext.h"
#include "ObjectGuid.h"
#include "Player.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "ScriptedCreature.h"
#include "SharedDefines.h"
#include "Spell.h"
#include "Timer.h"
#include "RaidNaxxSpellIds.h"
const uint32 NAXX_MAP_ID = 533;
template <class BossAiType>
class GenericBossHelper : public AiObject
{
public:
GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
virtual bool UpdateBossAI()
{
if (!bot->IsInCombat())
_unit = nullptr;
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
_unit = nullptr;
if (!_unit)
{
_unit = AI_VALUE2(Unit*, "find target", _name);
if (!_unit)
return false;
_target = _unit->ToCreature();
if (!_target)
return false;
_ai = dynamic_cast<BossAiType*>(_target->GetAI());
if (!_ai)
return false;
_event_map = &_ai->events;
if (!_event_map)
return false;
}
if (!_event_map)
return false;
_timer = getMSTime();
return true;
}
virtual void Reset()
{
_unit = nullptr;
_target = nullptr;
_ai = nullptr;
_event_map = nullptr;
_timer = 0;
}
protected:
std::string _name;
Unit* _unit = nullptr;
Creature* _target = nullptr;
BossAiType* _ai = nullptr;
EventMap* _event_map = nullptr;
uint32 _timer = 0;
};
class KelthuzadBossHelper : public AiObject
{
public:
KelthuzadBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
const std::pair<float, float> center = {3716.19f, -5106.58f};
const std::pair<float, float> tank_pos = {3709.19f, -5104.86f};
const std::pair<float, float> assist_tank_pos = {3746.05f, -5112.74f};
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
Reset();
if (!_unit)
_unit = AI_VALUE2(Unit*, "find target", "kel'thuzad");
return _unit != nullptr;
}
bool IsPhaseOne() { return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); }
bool IsPhaseTwo() { return _unit && !_unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE); }
Unit* GetAnyShadowFissure()
{
Unit* shadow_fissure = nullptr;
GuidVector units = *context->GetValue<GuidVector>("nearest triggers");
for (auto i = units.begin(); i != units.end(); i++)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
if (botAI->EqualLowercaseName(unit->GetName(), "shadow fissure"))
shadow_fissure = unit;
}
return shadow_fissure;
}
private:
void Reset() { _unit = nullptr; }
Unit* _unit = nullptr;
};
class RazuviousBossHelper : public AiObject
{
public:
RazuviousBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
Reset();
if (!_unit)
_unit = AI_VALUE2(Unit*, "find target", "instructor razuvious");
return _unit != nullptr;
}
private:
void Reset() { _unit = nullptr; }
Unit* _unit = nullptr;
};
class SapphironBossHelper : public AiObject
{
public:
const std::pair<float, float> mainTankPos = {3512.07f, -5274.06f};
const std::pair<float, float> center = {3517.31f, -5253.74f};
const float GENERIC_HEIGHT = 137.29f;
SapphironBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
Reset();
if (!_unit)
{
_unit = AI_VALUE2(Unit*, "find target", "sapphiron");
if (!_unit)
return false;
}
bool now_flying = _unit->IsFlying();
if (_was_flying && !now_flying)
_last_land_ms = getMSTime();
_was_flying = now_flying;
return true;
}
bool IsPhaseGround() { return _unit && !_unit->IsFlying(); }
bool IsPhaseFlight() { return _unit && _unit->IsFlying(); }
bool JustLanded()
{
if (!_last_land_ms)
return false;
return getMSTime() - _last_land_ms <= POSITION_TIME_AFTER_LANDED;
}
bool WaitForExplosion()
{
if (!IsPhaseFlight())
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member &&
(NaxxSpellIds::HasAnyAura(botAI, member, {NaxxSpellIds::Icebolt10, NaxxSpellIds::Icebolt25}) ||
botAI->HasAura("icebolt", member, false, false, -1, true)))
{
return true;
}
}
return false;
}
bool FindPosToAvoidChill(std::vector<float>& dest)
{
Aura* aura = NaxxSpellIds::GetAnyAura(bot, {NaxxSpellIds::Chill25});
if (!aura)
{
// Fallback to name for custom spell data.
aura = botAI->GetAura("chill", bot);
}
if (!aura)
return false;
DynamicObject* dyn_obj = aura->GetDynobjOwner();
if (!dyn_obj)
return false;
Unit* currentTarget = AI_VALUE(Unit*, "current target");
float angle = 0;
uint32 index = botAI->GetGroupSlotIndex(bot);
if (currentTarget)
{
if (botAI->IsRanged(bot))
{
if (bot->GetExactDist2d(currentTarget) <= 45.0f)
angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2;
else
{
if (index % 2 == 0)
angle = bot->GetAngle(currentTarget) + M_PI / 2;
else
angle = bot->GetAngle(currentTarget) - M_PI / 2;
}
}
else
{
if (index % 3 == 0)
angle = bot->GetAngle(currentTarget);
else if (index % 3 == 1)
angle = bot->GetAngle(currentTarget) + M_PI / 2;
else
angle = bot->GetAngle(currentTarget) - M_PI / 2;
}
}
else
angle = bot->GetAngle(dyn_obj) - M_PI + (rand_norm() - 0.5) * M_PI / 2;
dest = {bot->GetPositionX() + cos(angle) * 5.0f, bot->GetPositionY() + sin(angle) * 5.0f, bot->GetPositionZ()};
return true;
}
private:
void Reset()
{
_unit = nullptr;
_was_flying = false;
_last_land_ms = 0;
}
const uint32 POSITION_TIME_AFTER_LANDED = 5000;
Unit* _unit = nullptr;
bool _was_flying = false;
uint32 _last_land_ms = 0;
};
class GluthBossHelper : public AiObject
{
public:
const std::pair<float, float> mainTankPos25 = {3331.48f, -3109.06f};
const std::pair<float, float> mainTankPos10 = {3278.29f, -3162.06f};
const std::pair<float, float> beforeDecimatePos = {3267.34f, -3175.68f};
const std::pair<float, float> leftSlowDownPos = {3290.68f, -3141.65f};
const std::pair<float, float> rightSlowDownPos = {3300.78f, -3151.98f};
const std::pair<float, float> rangedPos = {3301.45f, -3139.29f};
const std::pair<float, float> healPos = {3303.09f, -3135.24f};
const float decimatedZombiePct = 10.0f;
GluthBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
Reset();
if (!_unit)
{
_unit = AI_VALUE2(Unit*, "find target", "gluth");
if (!_unit)
return false;
}
if (_unit->IsInCombat())
{
if (_combat_start_ms == 0)
_combat_start_ms = getMSTime();
}
else
_combat_start_ms = 0;
return true;
}
bool BeforeDecimate()
{
if (!_unit || !_unit->HasUnitState(UNIT_STATE_CASTING))
return false;
Spell* spell = _unit->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (!spell)
spell = _unit->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
if (!spell)
return false;
SpellInfo const* info = spell->GetSpellInfo();
if (!info)
return false;
if (NaxxSpellIds::MatchesAnySpellId(
info, {NaxxSpellIds::Decimate10, NaxxSpellIds::Decimate25, NaxxSpellIds::Decimate25Alt}))
return true;
// Fallback to name for custom spell data.
return info->SpellName[LOCALE_enUS] && botAI->EqualLowercaseName(info->SpellName[LOCALE_enUS], "decimate");
}
bool JustStartCombat() const { return _combat_start_ms != 0 && getMSTime() - _combat_start_ms < 10000; }
bool IsZombieChow(Unit* unit) const { return unit && botAI->EqualLowercaseName(unit->GetName(), "zombie chow"); }
private:
void Reset()
{
_unit = nullptr;
_combat_start_ms = 0;
}
Unit* _unit = nullptr;
uint32 _combat_start_ms = 0;
};
class LoathebBossHelper : public AiObject
{
public:
const std::pair<float, float> mainTankPos = {2877.57f, -3967.00f};
const std::pair<float, float> rangePos = {2896.96f, -3980.61f};
LoathebBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
Reset();
if (!_unit)
_unit = AI_VALUE2(Unit*, "find target", "loatheb");
return _unit != nullptr;
}
private:
void Reset() { _unit = nullptr; }
Unit* _unit = nullptr;
};
class FourhorsemanBossHelper : public AiObject
{
public:
const float posZ = 241.27f;
const std::pair<float, float> attractPos[2] = {{2502.03f, -2910.90f},
{2484.61f, -2947.07f}}; // left (sir zeliek), right (lady blaumeux)
FourhorsemanBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
else if (_combat_start_ms == 0)
_combat_start_ms = getMSTime();
if (_sir && (!_sir->IsInWorld() || !_sir->IsAlive()))
Reset();
if (!_sir)
{
_sir = AI_VALUE2(Unit*, "find target", "sir zeliek");
if (!_sir)
return false;
}
_lady = AI_VALUE2(Unit*, "find target", "lady blaumeux");
return true;
}
void Reset()
{
_sir = nullptr;
_lady = nullptr;
_combat_start_ms = 0;
posToGo = 0;
}
bool IsAttracter(Player* bot)
{
Difficulty diff = bot->GetRaidDifficulty();
if (diff == RAID_DIFFICULTY_25MAN_NORMAL)
{
return botAI->IsAssistRangedDpsOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 0) ||
botAI->IsAssistHealOfIndex(bot, 1) || botAI->IsAssistHealOfIndex(bot, 2);
}
return botAI->IsAssistRangedDpsOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 0);
}
void CalculatePosToGo(Player* bot)
{
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
Unit* lady = _lady;
if (!lady)
posToGo = 0;
else
{
uint32 elapsed_ms = _combat_start_ms ? getMSTime() - _combat_start_ms : 0;
// Interval: 24s - 15s - 15s - ...
posToGo = !(elapsed_ms <= 9000 || ((elapsed_ms - 9000) / 67500) % 2 == 0);
if (botAI->IsAssistRangedDpsOfIndex(bot, 0) || (raid25 && botAI->IsAssistHealOfIndex(bot, 1)))
posToGo = 1 - posToGo;
}
}
std::pair<float, float> CurrentAttractPos()
{
bool raid25 = bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL;
float posX = attractPos[posToGo].first, posY = attractPos[posToGo].second;
if (posToGo == 1)
{
float offset_x = 0.0f;
float offset_y = 0.0f;
float bias = 4.5f;
if (raid25)
{
offset_x = -bias;
offset_y = bias;
}
posX += offset_x;
posY += offset_y;
}
return {posX, posY};
}
Unit* CurrentAttackTarget()
{
if (posToGo == 0)
return _sir;
return _lady;
}
protected:
Unit* _sir = nullptr;
Unit* _lady = nullptr;
uint32 _combat_start_ms = 0;
int posToGo = 0;
};
class ThaddiusBossHelper : public AiObject
{
public:
const std::pair<float, float> tankPosFeugen = {3522.94f, -3002.60f};
const std::pair<float, float> tankPosStalagg = {3436.14f, -2919.98f};
const std::pair<float, float> rangedPosFeugen = {3500.45f, -2997.92f};
const std::pair<float, float> rangedPosStalagg = {3441.01f, -2942.04f};
const float tankPosZ = 312.61f;
ThaddiusBossHelper(PlayerbotAI* botAI) : AiObject(botAI) {}
bool UpdateBossAI()
{
if (!bot->IsInCombat())
Reset();
if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
Reset();
if (!_unit)
{
_unit = AI_VALUE2(Unit*, "find target", "thaddius");
if (!_unit)
return false;
}
feugen = AI_VALUE2(Unit*, "find target", "feugen");
stalagg = AI_VALUE2(Unit*, "find target", "stalagg");
return true;
}
bool IsPhasePet() { return (feugen && feugen->IsAlive()) || (stalagg && stalagg->IsAlive()); }
bool IsPhaseTransition()
{
if (IsPhasePet())
return false;
return _unit && _unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
}
bool IsPhaseThaddius() { return !IsPhasePet() && !IsPhaseTransition(); }
Unit* GetNearestPet()
{
Unit* unit = nullptr;
if (feugen && feugen->IsAlive())
unit = feugen;
if (stalagg && stalagg->IsAlive() && (!feugen || bot->GetDistance(stalagg) < bot->GetDistance(feugen)))
unit = stalagg;
return unit;
}
std::pair<float, float> PetPhaseGetPosForTank()
{
if (GetNearestPet() == feugen)
return tankPosFeugen;
return tankPosStalagg;
}
std::pair<float, float> PetPhaseGetPosForRanged()
{
if (GetNearestPet() == feugen)
return rangedPosFeugen;
return rangedPosStalagg;
}
protected:
void Reset()
{
_unit = nullptr;
feugen = nullptr;
stalagg = nullptr;
}
Unit* _unit = nullptr;
Unit* feugen = nullptr;
Unit* stalagg = nullptr;
};
#endif

View File

@ -1,165 +0,0 @@
#ifndef _PLAYERBOT_RAIDNAXXSPELLIDS_H
#define _PLAYERBOT_RAIDNAXXSPELLIDS_H
#include <initializer_list>
#include "PlayerbotAI.h"
// use src/server/scripts/Northrend/Naxxramas/naxxramas.h for CreatureId, NaxxramasSay, NaxxramasEvent, NaxxramasMisc
namespace NaxxSpellIds
{
// Heigan
static constexpr uint32 Eruption10 = 29371;
/*
SPELL_SPELL_DISRUPTION = 29310,
SPELL_DECREPIT_FEVER = 29998,
SPELL_PLAGUE_CLOUD = 29350,
SPELL_TELEPORT_SELF = 30211
*/
// Grobbulus
static constexpr uint32 PoisonCloud = 28240;
// Thaddius polarity
static constexpr uint32 PositiveCharge10 = 28059;
static constexpr uint32 PositiveCharge25 = 28062;
static constexpr uint32 PositiveChargeStack = 29659;
static constexpr uint32 NegativeCharge10 = 28084;
static constexpr uint32 NegativeCharge25 = 28085;
static constexpr uint32 NegativeChargeStack = 29660;
/*
SPELL_MAGNETIC_PULL = 28337,
SPELL_TESLA_SHOCK = 28099,
SPELL_SHOCK_VISUAL = 28159,
// Stalagg
SPELL_POWER_SURGE = 54529,
SPELL_STALAGG_CHAIN = 28096,
// Feugen
SPELL_STATIC_FIELD = 28135,
SPELL_FEUGEN_CHAIN = 28111,
// Thaddius
SPELL_POLARITY_SHIFT = 28089,
SPELL_BALL_LIGHTNING = 28299,
SPELL_CHAIN_LIGHTNING = 28167,
SPELL_BERSERK = 27680,
SPELL_THADDIUS_VISUAL_LIGHTNING = 28136,
SPELL_THADDIUS_SPAWN_STUN = 28160,
SPELL_POSITIVE_CHARGE = 28062,
SPELL_POSITIVE_CHARGE_STACK = 29659,
SPELL_NEGATIVE_CHARGE = 28085,
SPELL_NEGATIVE_CHARGE_STACK = 29660,
SPELL_POSITIVE_POLARITY = 28059,
SPELL_NEGATIVE_POLARITY = 28084
*/
// Sapphiron
static constexpr uint32 Icebolt10 = 28522;
static constexpr uint32 Icebolt25 = 28526;
static constexpr uint32 Chill25 = 55699;
/*
// Fight
SPELL_FROST_AURA = 28531,
SPELL_CLEAVE = 19983,
SPELL_TAIL_SWEEP = 55697,
SPELL_SUMMON_BLIZZARD = 28560,
SPELL_LIFE_DRAIN = 28542,
SPELL_BERSERK = 26662,
// Ice block
SPELL_ICEBOLT_CAST = 28526,
SPELL_ICEBOLT_TRIGGER = 28522,
SPELL_FROST_MISSILE = 30101,
SPELL_FROST_EXPLOSION = 28524,
// Visuals
SPELL_SAPPHIRON_DIES = 29357
*/
// Gluth
static constexpr uint32 Decimate10 = 28374;
static constexpr uint32 Decimate25 = 54426;
static constexpr uint32 Decimate25Alt = 28375;
static constexpr uint32 MortalWound10 = 25646;
static constexpr uint32 MortalWound25 = 54378;
/*
SPELL_MORTAL_WOUND = 25646,
SPELL_ENRAGE = 28371,
SPELL_DECIMATE = 28374,
SPELL_DECIMATE_DAMAGE = 28375,
SPELL_BERSERK = 26662,
SPELL_INFECTED_WOUND = 29306,
SPELL_CHOW_SEARCHER = 28404
*/
// Anub'Rekhan
static constexpr uint32 LocustSwarm10 = 28785;
static constexpr uint32 LocustSwarm10Alt = 28786;
static constexpr uint32 LocustSwarm25 = 54021; // 25-man Locust Swarm
/*
SPELL_IMPALE = 28783,
SPELL_LOCUST_SWARM = 28785,
SPELL_SUMMON_CORPSE_SCARABS_5 = 29105,
SPELL_SUMMON_CORPSE_SCARABS_10 = 28864,
SPELL_BERSERK = 26662
ACHIEV_TIMED_START_EVENT = 9891,
EVENT_SPAWN_CRYPT_GUARDS_1 = 0,
EVENT_BERSERK = 1,
////
Position const cryptguardPositions[] = {
{ 3299.732f, -3502.489f, 287.077f, 2.378f },
{ 3299.086f, -3450.929f, 287.077f, 3.999f },
{ 3331.217f, -3476.607f, 287.074f, 3.269f }
};
*/
// Loatheb
static constexpr uint32 NecroticAura10 = 55593;
/*
SPELL_NECROTIC_AURA = 55593,
SPELL_SUMMON_SPORE = 29234,
SPELL_DEATHBLOOM = 29865,
SPELL_INEVITABLE_DOOM = 29204,
SPELL_BERSERK = 26662
*/
inline bool HasAnyAura(PlayerbotAI* botAI, Unit* unit, std::initializer_list<uint32> spellIds)
{
if (!botAI || !unit)
return false;
for (uint32 spellId : spellIds)
{
if (botAI->HasAura(spellId, unit))
return true;
}
return false;
}
inline Aura* GetAnyAura(Unit* unit, std::initializer_list<uint32> spellIds)
{
if (!unit)
return nullptr;
for (uint32 spellId : spellIds)
{
if (Aura* aura = unit->GetAura(spellId))
return aura;
}
return nullptr;
}
inline bool MatchesAnySpellId(SpellInfo const* info, std::initializer_list<uint32> spellIds)
{
if (!info)
return false;
for (uint32 spellId : spellIds)
{
if (info->Id == spellId)
return true;
}
return false;
}
} // namespace NaxxSpellIds
#endif

View File

@ -8,7 +8,6 @@
#include "RaidKarazhanStrategy.h" #include "RaidKarazhanStrategy.h"
#include "RaidGruulsLairStrategy.h" #include "RaidGruulsLairStrategy.h"
#include "RaidMagtheridonStrategy.h" #include "RaidMagtheridonStrategy.h"
#include "RaidNaxxStrategy.h"
#include "RaidSSCStrategy.h" #include "RaidSSCStrategy.h"
#include "RaidTempestKeepStrategy.h" #include "RaidTempestKeepStrategy.h"
#include "RaidOsStrategy.h" #include "RaidOsStrategy.h"
@ -29,7 +28,6 @@ public:
creators["karazhan"] = &RaidStrategyContext::karazhan; creators["karazhan"] = &RaidStrategyContext::karazhan;
creators["gruulslair"] = &RaidStrategyContext::gruulslair; creators["gruulslair"] = &RaidStrategyContext::gruulslair;
creators["magtheridon"] = &RaidStrategyContext::magtheridon; creators["magtheridon"] = &RaidStrategyContext::magtheridon;
creators["naxx"] = &RaidStrategyContext::naxx;
creators["ssc"] = &RaidStrategyContext::ssc; creators["ssc"] = &RaidStrategyContext::ssc;
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep; creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
@ -47,7 +45,6 @@ private:
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); } static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); } static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); }
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); } static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); } static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }

View File

@ -41,8 +41,6 @@
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h" #include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h" #include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
#include "Ai/Raid/Naxxramas/RaidNaxxActionContext.h"
#include "Ai/Raid/Naxxramas/RaidNaxxTriggerContext.h"
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h" #include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
@ -121,7 +119,6 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new RaidKarazhanActionContext()); actionContexts.Add(new RaidKarazhanActionContext());
actionContexts.Add(new RaidGruulsLairActionContext()); actionContexts.Add(new RaidGruulsLairActionContext());
actionContexts.Add(new RaidMagtheridonActionContext()); actionContexts.Add(new RaidMagtheridonActionContext());
actionContexts.Add(new RaidNaxxActionContext());
actionContexts.Add(new RaidSSCActionContext()); actionContexts.Add(new RaidSSCActionContext());
actionContexts.Add(new RaidTempestKeepActionContext()); actionContexts.Add(new RaidTempestKeepActionContext());
actionContexts.Add(new RaidOsActionContext()); actionContexts.Add(new RaidOsActionContext());
@ -158,7 +155,6 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new RaidKarazhanTriggerContext()); triggerContexts.Add(new RaidKarazhanTriggerContext());
triggerContexts.Add(new RaidGruulsLairTriggerContext()); triggerContexts.Add(new RaidGruulsLairTriggerContext());
triggerContexts.Add(new RaidMagtheridonTriggerContext()); triggerContexts.Add(new RaidMagtheridonTriggerContext());
triggerContexts.Add(new RaidNaxxTriggerContext());
triggerContexts.Add(new RaidSSCTriggerContext()); triggerContexts.Add(new RaidSSCTriggerContext());
triggerContexts.Add(new RaidTempestKeepTriggerContext()); triggerContexts.Add(new RaidTempestKeepTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext()); triggerContexts.Add(new RaidOsTriggerContext());

View File

@ -1550,9 +1550,6 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 532: case 532:
strategyName = "karazhan"; // Karazhan strategyName = "karazhan"; // Karazhan
break; break;
case 533:
strategyName = "naxx"; // Naxxramas
break;
case 544: case 544:
strategyName = "magtheridon"; // Magtheridon's Lair strategyName = "magtheridon"; // Magtheridon's Lair
break; break;

View File

@ -2255,7 +2255,10 @@ void RandomItemMgr::BuildEquipCacheNew()
continue; continue;
} }
if (sPlayerbotAIConfig.unobtainableItems.find(itemId) != sPlayerbotAIConfig.unobtainableItems.end()) // Unobtainable or unusable items
if (itemId == 12468 || // Chilton Wand
itemId == 22784 || // Sunwell Orb
itemId == 46978) // Totem of the Earthen Ring
continue; continue;
equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId); equipCacheNew[proto->RequiredLevel][proto->InventoryType].push_back(itemId);

View File

@ -180,13 +180,8 @@ bool PlayerbotAIConfig::Initialize()
"165739,165738,175245,175970,176325,176327,123329,2560"), "165739,165738,175245,175970,176325,176327,123329,2560"),
disallowedGameObjects); disallowedGameObjects);
LoadSet<std::set<uint32>>( LoadSet<std::set<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901,10888,10445,10985"), sConfigMgr->GetOption<std::string>("AiPlayerbot.AttunementQuests", "10279,10277,10282,10283,10284,10285,10296,10297,10298,11481,11482,11488,11490,11492,10901"),
attunementQuests); attunementQuests);
LoadSet<std::set<uint32>>(
sConfigMgr->GetOption<std::string>("AiPlayerbot.UnobtainableItems", "12468,46978"),
unobtainableItems);
botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false); botAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true); randomBotAutologin = sConfigMgr->GetOption<bool>("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500); minRandomBots = sConfigMgr->GetOption<int32>("AiPlayerbot.MinRandomBots", 500);

View File

@ -99,7 +99,6 @@ public:
bool tellWhenAvoidAoe; bool tellWhenAvoidAoe;
std::set<uint32> disallowedGameObjects; std::set<uint32> disallowedGameObjects;
std::set<uint32> attunementQuests; std::set<uint32> attunementQuests;
std::set<uint32> unobtainableItems;
uint32 openGoSpell; uint32 openGoSpell;
bool randomBotAutologin; bool randomBotAutologin;